Index: webform.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/webform.module,v
retrieving revision 1.113.2.70.2.38
diff -u -r1.113.2.70.2.38 webform.module
--- webform.module	3 Apr 2008 03:52:54 -0000	1.113.2.70.2.38
+++ webform.module	11 Apr 2008 23:09:41 -0000
@@ -13,7 +13,7 @@
  */
 
 /**
- * Implemenation of hook_help().
+ * Implementation of hook_help().
  */
 function webform_help($section = "admin/help#webform") {
   $output =  "";
@@ -114,6 +114,13 @@
         'access' => node_access('view', $node),
         'type' => MENU_CALLBACK,
       );
+      $items[]= array(
+        'path' => 'node/'. $node->nid .'/draft_saved',
+        'title' => t('Draft saved'),
+        'callback' => '_webform_draft_confirmation',
+        'callback arguments' => array(arg(1)),
+        'type' => MENU_CALLBACK,
+      );
       $items[] = array(
         'path' => 'node/'. $nid .'/done',
         'title' => t('Webform confirmation'),
@@ -209,7 +216,7 @@
           'path' => 'node/'. $nid .'/submission/'. $sid,
           'title' => t('Webform submission'),
           'callback' => 'drupal_get_form',
-          'callback arguments' => array('webform_client_form_'. $node->nid, $node, $submission, FALSE, FALSE),
+          'callback arguments' => array('webform_client_form_'. $node->nid, $node, $submission, FALSE, FALSE, FALSE),
           'access' => webform_submission_access($node, $submission, 'view'),
           'type' => MENU_CALLBACK,
         );
@@ -217,7 +224,7 @@
           'path' => 'node/'. $nid .'/submission/'. $sid .'/view',
           'title' => t('View'),
           'callback' => 'drupal_get_form',
-          'callback arguments' => array('webform_client_form_'. $node->nid, $node, $submission, FALSE, FALSE),
+          'callback arguments' => array('webform_client_form_'. $node->nid, $node, $submission, FALSE, FALSE, FALSE),
           'access' => webform_submission_access($node, $submission, 'view'),
           'weight' => 0,
           'type' => MENU_DEFAULT_LOCAL_TASK,
@@ -226,7 +233,7 @@
           'path' => 'node/'. $nid .'/submission/'. $sid .'/edit',
           'title' => t('Edit'),
           'callback' => 'drupal_get_form',
-          'callback arguments' => array('webform_client_form_'. $node->nid, $node, $submission, TRUE, FALSE),
+          'callback arguments' => array('webform_client_form_'. $node->nid, $node, $submission, TRUE, FALSE, FALSE),
           'access' => webform_submission_access($node, $submission, 'edit'),
           'weight' => 1,
           'type' => MENU_LOCAL_TASK,
@@ -268,7 +275,7 @@
 }
 
 /**
- * Implemenation of hook_access().
+ * Implementation of hook_access().
  */
 function webform_access($op, $node) {
   global $user;
@@ -308,13 +315,13 @@
 }
 
 /**
- * Implemenation of hook_insert().
+ * Implementation of hook_insert().
  */
 function webform_insert($node) {
   include_once(drupal_get_path('module', 'webform') .'/webform_components.inc');
 
   // Insert the Webform.
-  db_query("INSERT INTO {webform} (nid, confirmation, teaser, submit_text, submit_limit, submit_interval, email, email_from_name, email_from_address, email_subject, additional_validate, additional_submit) VALUES (%d, '%s', %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s')", $node->nid, $node->webform['confirmation'], $node->webform['teaser'], $node->webform['submit_text'], $node->webform['submit_limit'], $node->webform['submit_interval'], $node->webform['email'], $node->webform['email_from_name'], $node->webform['email_from_address'], $node->webform['email_subject'], $node->webform['additional_validate'], $node->webform['additional_submit']);
+  db_query("INSERT INTO {webform} (nid, confirmation, teaser, submit_text, submit_limit, submit_interval, email, email_from_name, email_from_address, email_subject, additional_validate, additional_submit, allow_draft) VALUES (%d, '%s', %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', %d)", $node->nid, $node->webform['confirmation'], $node->webform['teaser'], $node->webform['submit_text'], $node->webform['submit_limit'], $node->webform['submit_interval'], $node->webform['email'], $node->webform['email_from_name'], $node->webform['email_from_address'], $node->webform['email_subject'], $node->webform['additional_validate'], $node->webform['additional_submit'], $node->webform['allow_draft']);
 
   // Insert the components into the database.
   if (isset($node->webform['components']) && !empty($node->webform['components'])) {
@@ -327,7 +334,7 @@
 }
 
 /**
- * Implemenation of hook_update().
+ * Implementation of hook_update().
  */
 function webform_update($node) {
   // Update the webform by deleting existing data and replacing with the new.
@@ -337,7 +344,7 @@
 }
 
 /**
- * Implemenation of hook_delete().
+ * Implementation of hook_delete().
  */
 function webform_delete(&$node) {
   db_query("DELETE FROM {webform} WHERE nid = %d", $node->nid);
@@ -625,6 +632,13 @@
     '#description' => t('By default the submit button on this form will have the label <em>Submit</em>. Enter a new title here to override the default.'),
   );
 
+  // Allow save draft
+  $form['webform']['advanced']['allow_draft'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Allow users to save a draft'),
+    '#default_value' => $node->webform['allow_draft'],
+  );
+  
   if (user_access('use PHP for additional processing')) {
     $form['webform']['advanced']['additional_validate'] = array(
       '#type' => 'textarea',
@@ -830,7 +844,7 @@
   $preview = FALSE;
 
   if ($_POST['op'] == t('Preview')) {
-    $preview = true;
+    $preview = TRUE;
     $additions = webform_load($node);
     $node->webform['components'] = $additions->webform['components'];
   }
@@ -848,7 +862,18 @@
     }
   }
 
-  $output = drupal_get_form('webform_client_form_'. $node->nid, $node, $submission, $enabled, $preview);
+  // Check if this user has a draft for this webform.
+  $is_draft = FALSE;
+  if ($node->webform['allow_draft'] && $user->uid != 0) {
+    // Draft found - display form with draft data for further editing.
+    if ($_draft_sid = _webform_fetch_draft_sid($node->nid, $user->uid)) {
+      include_once(drupal_get_path('module', 'webform') ."/webform_submissions.inc");
+      $submission = webform_get_submission($node->nid, $_draft_sid);
+      $enabled = TRUE;
+      $is_draft = TRUE;
+    }
+  }
+  $output = drupal_get_form('webform_client_form_'. $node->nid, $node, $submission, $enabled, $preview, $is_draft);
 
   // Remove the surrounding <form> tag if this is a preview.
   if ($preview) {
@@ -859,7 +884,7 @@
   $node->content['webform'] = array('#value' => $output, '#weight' => 1);
 
   if (user_access('access own webform submissions') && !(user_access('access webform results') || user_access('access webform submissions')) && $user->uid) {
-    $previous_submissions = db_result(db_query("SELECT count(*) FROM {webform_submissions} WHERE nid = %d AND uid = %d", $node->nid, $user->uid));
+    $previous_submissions = db_result(db_query("SELECT count(*) FROM {webform_submissions} WHERE nid = %d AND uid = %d AND is_draft = false", $node->nid, $user->uid));
     if ($previous_submissions) {
       drupal_set_message(t('You have already submitted this form. <a href="!url">View your previous submissions</a>.', array('!url' => url('node/'. $node->nid .'/submissions'))));
     }
@@ -977,19 +1002,20 @@
  * @param $submission
  *   An array of values for the form if we're displaying a result.
  * @param $enabled
- *   If displaying a result, specify if form elements are enabled for
- *   editing.
+ *   If displaying a result, specify if form elements are enabled for editing.
+ * @param $is_draft
+ *   Optional. Set to TRUE if displaying a draft.
  * @param $form_values
  *   The current form values of a submission, used in multipage webforms.
  *   Note: The position of this parameter depends on all other parameters being
  *   specified when using drupal_get_form(). 
  */
-function webform_client_form(&$node, $submission, $enabled, $preview, $form_values = NULL) {
+function webform_client_form(&$node, $submission, $enabled, $preview, $is_draft = FALSE, $form_values = NULL) {
   global $user;
   include_once(drupal_get_path('module', 'webform') .'/webform_components.inc');
   webform_load_components();
 
-  if (isset($submission['sid'])) {
+  if (isset($submission['sid']) && !$is_draft) {
     drupal_set_title(t('Submission #@sid', array('@sid' => $submission['sid'])));
   }
 
@@ -999,48 +1025,50 @@
 
   // Set a header for navigating results.
   if ($submission && user_access('access webform results')) {
-    // Add CSS to display submission info. Don't preprocess because this CSS file is used rarely.
-    drupal_add_css(drupal_get_path('module', 'webform') .'/webform.css', 'module', 'all', FALSE);
-
-    $previous = db_result(db_query('SELECT MAX(sid) FROM {webform_submissions} WHERE nid = %d AND sid < %d', array($node->nid, $submission['sid'])));
-    $next = db_result(db_query('SELECT MIN(sid) FROM {webform_submissions} WHERE nid = %d AND sid > %d', array($node->nid, $submission['sid'])));
-
-    $form['submission'] = array(
-      '#type' => 'value',
-      '#value' => $submission,
-    );
-    $form['navigation'] = array(
-      '#prefix' => '<div class="webform-submission-navigation">',
-      '#suffix' => '</div>',
-    );
-    $form['navigation']['previous'] = array(
-      '#value' => $previous ? l(t('Previous submission'), 'node/'. $node->nid .'/submission/'. $previous . ($enabled ? '/edit' : '') , array('class' => 'webform-submission-previous'), ($enabled ? 'destination=node/'. $node->nid .'/submission/'. $previous .'/edit' : NULL)) : '<span class="webform-submission-previous">'. t('Previous submission') .'</span>',
-    );
-    $form['navigation']['next'] = array(
-      '#value' => $next ? l(t('Next submission'), 'node/'. $node->nid .'/submission/'. $next . ($enabled ? '/edit' : ''), array('class' => 'webform-submission-next'), ($enabled ? 'destination=node/'. $node->nid .'/submission/'. $next .'/edit' : NULL)) : '<span class="webform-submission-next">'. t('Next submission') .'</span>',
-    );
+    if (!$is_draft) {
+      // Add CSS to display submission info. Don't preprocess because this CSS file is used rarely.
+      drupal_add_css(drupal_get_path('module', 'webform') .'/webform.css', 'module', 'all', FALSE);
+
+      $previous = db_result(db_query('SELECT MAX(sid) FROM {webform_submissions} WHERE nid = %d AND sid < %d', array($node->nid, $submission['sid'])));
+      $next = db_result(db_query('SELECT MIN(sid) FROM {webform_submissions} WHERE nid = %d AND sid > %d', array($node->nid, $submission['sid'])));
+
+      $form['navigation'] = array(
+        '#prefix' => '<div class="webform-submission-navigation">',
+        '#suffix' => '</div>',
+      );
+      $form['navigation']['previous'] = array(
+        '#value' => $previous ? l(t('Previous submission'), 'node/'. $node->nid .'/submission/'. $previous . ($enabled ? '/edit' : '') , array('class' => 'webform-submission-previous'), ($enabled ? 'destination=node/'. $node->nid .'/submission/'. $previous .'/edit' : NULL)) : '<span class="webform-submission-previous">'. t('Previous submission') .'</span>',
+      );
+      $form['navigation']['next'] = array(
+        '#value' => $next ? l(t('Next submission'), 'node/'. $node->nid .'/submission/'. $next . ($enabled ? '/edit' : ''), array('class' => 'webform-submission-next'), ($enabled ? 'destination=node/'. $node->nid .'/submission/'. $next .'/edit' : NULL)) : '<span class="webform-submission-next">'. t('Next submission') .'</span>',
+      );
 
-    $form['submission_info'] = array(
-      '#title' => t('Submission Information'),
-      '#type' => 'fieldset',
-      '#collapsible' => FALSE,
-    );
-    $account = user_load(array('uid' => $submission['uid']));
-    $form['submission_info']['user_picture'] = array(
-      '#value' => theme('user_picture', $account),
-    );
-    $form['submission_info']['form'] = array(
-      '#value' => '<div>'. t('Form: !form', array('!form' => l($node->title, 'node/'. $node->nid))) .'</div>',
-    );
-    $form['submission_info']['submitted'] = array(
-      '#value' => '<div>'. t('Submitted by !name', array('!name' => theme('username', $account))) .'</div>',
-    );
-    $form['submission_info']['time'] = array(
-      '#value' => '<div>'. format_date($submission['submitted'], 'large') .'</div>',
-    );
-    $form['submission_info']['ip_address'] = array(
-      '#value' => '<div>'. $submission['remote_addr'] .'</div>',
-    );
+      $form['submission'] = array(
+        '#type' => 'value',
+        '#value' => $submission,
+      );
+      $form['submission_info'] = array(
+        '#title' => t('Submission Information'),
+        '#type' => 'fieldset',
+        '#collapsible' => FALSE,
+      );
+      $account = user_load(array('uid' => $submission['uid']));
+      $form['submission_info']['user_picture'] = array(
+        '#value' => theme('user_picture', $account),
+      );
+      $form['submission_info']['form'] = array(
+        '#value' => '<div>'. t('Form: !form', array('!form' => l($node->title, 'node/'. $node->nid))) .'</div>',
+      );
+      $form['submission_info']['submitted'] = array(
+        '#value' => '<div>'. t('Submitted by !name', array('!name' => theme('username', $account))) .'</div>',
+      );
+      $form['submission_info']['time'] = array(
+        '#value' => '<div>'. format_date($submission['submitted'], 'large') .'</div>',
+      );
+      $form['submission_info']['ip_address'] = array(
+        '#value' => '<div>'. $submission['remote_addr'] .'</div>',
+      );
+    }
   }
 
   // Add a theme function for this form.
@@ -1129,12 +1157,21 @@
           '#weight' => 1000,
         );
       }
+      // Save draft button
+      if ($node->webform['allow_draft'] && $user->uid != 0) {
+        $form['draftbutton'] = array(
+          '#type' => 'submit',
+          '#value' => t('Save Draft'),
+          '#weight' => 999,
+        );
+        $form['#no_validate_on'] = 'op=Save Draft';
+      }
     }
 
     // Recursively add components to the form. Microweights keep thins in webform order.
     $microweight = 0.001;
     foreach ($component_tree['children'] as $cid => $component) {
-      _webform_client_form_add_component($cid, $component, $form['submitted'], $form, $submission, $page_num, $enabled);
+      _webform_client_form_add_component($cid, $component, $form['submitted'], $form, $submission, $page_num, $enabled, (int) $node->webform['allow_draft'] && $user->uid != 0);
       $form['submitted'][$component['form_key']]['#weight'] += $microweight;
       $microweight += 0.001;
     }
@@ -1169,7 +1206,7 @@
   return $form;
 }
 
-function _webform_client_form_add_component($cid, $component, &$parent_fieldset, &$form, $submission, $page_num, $enabled = false) {
+function _webform_client_form_add_component($cid, $component, &$parent_fieldset, &$form, $submission, $page_num, $enabled = FALSE, $allow_draft = 0) {
   // Load with submission information if necessary.
   if (!empty($submission) && !$enabled) {
     // This component is display only, with the value set according information
@@ -1197,6 +1234,26 @@
       }
     }
   }
+
+  // Save drafts: If the webform is being built during a submission, and draft save
+  // has been requested, disable required fields to prevent validation warning.
+  // @todo This leaves the form elements in the rendered page without the 'required'
+  // markers. At the moment, that undesirable result is circumvented by having
+  // save-draft submissions redirect to a status page w/o the form. Also, this only
+  // disables 'required' field validation. Need to test/develop for other validation.
+  $validate = TRUE;
+  if (isset($form['#no_validate_on'])) {
+    list($no_validate_key, $no_validate_value) = explode('=', $form['#no_validate_on']);
+    if ($_POST[$no_validate_key] == $no_validate_value) {
+      $validate = FALSE;
+    }
+  }
+
+  if (!$validate) {
+    $parent_fieldset[$component['form_key']]['#required'] = 0;
+    unset($form['#redirect']);
+  }
+
   if (isset($component['children']) && is_array($component['children'])) {
     $microweight = 0.001;
     foreach ($component['children'] as $scid => $subcomponent) {
@@ -1257,7 +1314,7 @@
   $session_key = 'webform_form_'. $node->nid;
 
   // Check for a multi-page form that is not yet complete.
-  if ($form_values['op'] != (empty($node->webform['submit_text']) ? t('Submit') : $node->webform['submit_text'])) {
+  if ($form_values['op'] != (empty($node->webform['submit_text']) ? t('Submit') : $node->webform['submit_text']) && $form_values['op'] != t('Save Draft')) {
     // This is a multi-page form that is not yet complete.
     // Save the order of the current post into a copied array.
     $original_post = is_array($_POST['submitted']) ? $_POST['submitted'] : array();
@@ -1332,19 +1389,27 @@
     eval("?>". $node->webform['additional_submit']);
   }
 
+  // Check if user is submitting as a draft
+  $is_draft = ($node->webform['allow_draft'] && $form_values['op'] == t('Save Draft')) ? 1 : 0;
+
   // Save the submission to the database.
   if (!$form_values['details']['sid']) {
     // No sid was found thus insert it in the datatabase.
-    $form_values['details']['sid'] = webform_submission_insert($node, $form_values['submitted']);
+    $form_values['details']['sid'] = webform_submission_insert($node, $form_values['submitted'], $is_draft);
     $form_values['details']['is_new'] = TRUE;
   }
   else {
     // Sid was found thus update the existing sid in the datatbase.
-    webform_submission_update($node, $form_values['details']['sid'], $form_values['submitted']);
+    webform_submission_update($node, $form_values['details']['sid'], $form_values['submitted'], $is_draft);
     $form_values['details']['is_new'] = FALSE;
   }
 
   $sid = $form_values['details']['sid'];
+  
+  // If saving a draft, return redirect to confirmation page (no email processing).
+  if ($node->webform['allow_draft'] && $form_values['op'] == t('Save Draft')) {
+    return array('node/'. $node->nid .'/draft_saved', 'sid='. $sid);
+  }
 
   // Check if this form is sending an email.
   if ((!empty($node->webform['email']) || !empty($node->webform['additional_emails'])) && $form_values['details']['is_new']) {
@@ -1476,7 +1541,7 @@
   if (is_array($form_values)) {
     foreach ($form_values as $form_key => $value) {
       $cid = webform_get_cid($node, $form_key, $parent);
-      if (is_array($value) && $node->webform['components'][$cid]['type'] == 'fieldset') {
+      if ($cid && is_array($value) && $node->webform['components'][$cid]['type'] == 'fieldset') {
         _webform_client_form_submit_process($node, $form_values[$form_key], $cid);
       }
     }
@@ -1510,7 +1575,9 @@
       else {
         // The order here is significant!
         unset($form[$form_key]);
-        $form[$cid] = $value;
+        if ($cid) {
+          $form[$cid] = $value;
+        }
       }
     }
   }
@@ -1535,7 +1602,6 @@
  *   The webform node that has just been submitted.
  */
 function theme_webform_confirmation($node, $sid) {
-
   if (empty($node->webform['confirmation'])) {
     drupal_set_message(t('Thank you, your submission has been received.'));
     drupal_goto('node/'. $node->nid);
@@ -1550,6 +1616,47 @@
 }
 
 /**
+ * Prints the confirmation message after a successful draft submission.
+ */
+function _webform_draft_confirmation($nid) {
+  if ($node = node_load($nid)) {
+    if (node_access('view', $node)) {
+      drupal_set_title($node->title);
+      drupal_set_message(t('Draft saved.'));
+      return theme('webform_draft_confirmation', $node);
+    }
+    else {
+      return drupal_access_denied();
+    }
+  }
+}
+
+/**
+ * Themable function for webform draft confirmation
+ */
+function theme_webform_draft_confirmation($node) {
+  $node->body = check_markup('Your draft has been successfully saved. ', $node->format, FALSE);
+  $node->links['webform_back'] = array(
+    'href' => 'node/'. $node->nid,
+    'title' => t('Go back to the form'),
+  );
+  return theme('node', $node, FALSE, TRUE);
+}
+
+/**
+ * Check if current user has a draft of this webform, and return the sid.
+ */
+function _webform_fetch_draft_sid($nid, $uid) {
+  $query = 'SELECT sid FROM {webform_submissions} WHERE nid = %d AND uid = %d AND is_draft = 1 ORDER BY submitted DESC';
+  $res = db_query($query, $nid, $uid);
+  $row = db_fetch_array($res);
+  if (isset($row['sid'])) {
+    return (int) $row['sid'];
+  }
+  return FALSE;
+}
+
+/**
  * Theme the contents of e-mails sent by webform.
  * 
  * @param $form_values
Index: webform_submissions.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/webform_submissions.inc,v
retrieving revision 1.1.2.11
diff -u -r1.1.2.11 webform_submissions.inc
--- webform_submissions.inc	1 Apr 2008 05:41:41 -0000	1.1.2.11
+++ webform_submissions.inc	11 Apr 2008 17:09:06 -0000
@@ -23,9 +23,24 @@
   }  
 }
 
-function webform_submission_update($node, $sid, $submitted) {
-  // Update the submission data by first removing all this submissions data.
-  db_query("DELETE FROM {webform_submitted_data} WHERE sid = %d", $sid);
+function webform_submission_update($node, $sid, $submitted, $is_draft = 0) {
+ global $user;
+
+   db_query("DELETE FROM {webform_submissions} WHERE sid = %d", $sid);
+  db_query("INSERT INTO {webform_submissions} (nid, sid, uid, submitted, remote_addr, is_draft) "." VALUES (%d, %d, %d, %d, '%s', %d)", $node->nid, $sid, $user->uid, time(), $_SERVER['REMOTE_ADDR'], $is_draft);
+
+  // If is draft, only delete data for components submitted, to
+  // preserve any data from form pages not visited in this submission.
+  if ($is_draft) {
+    $_submitted_cids = array();
+    foreach ($submitted as $cid => $value) {
+      $_submitted_cids[] = $cid;
+    }
+    db_query("DELETE FROM {webform_submitted_data} WHERE sid = %d AND cid IN (" . implode(', ', $_submitted_cids) . ")", $sid);
+  } else {
+    db_query("DELETE FROM {webform_submitted_data} WHERE sid = %d", $sid);
+  }
+
   // Then re-add it to the database.
   foreach ($submitted as $cid => $value) {
     // Don't save pagebreaks as submitted data.
@@ -48,12 +63,12 @@
   return $sid;
 }
 
-function webform_submission_insert($node, $submitted) {
+function webform_submission_insert($node, $submitted, $is_draft = 0) {
   global $user;
 
   $sid = db_next_id('{webform_submissions}_sid');
 
-  db_query("INSERT INTO {webform_submissions} (nid, sid, uid, submitted, remote_addr) "." VALUES (%d, %d, %d, %d, '%s')", $node->nid, $sid, $user->uid, time(), $_SERVER['REMOTE_ADDR']);
+  db_query("INSERT INTO {webform_submissions} (nid, sid, uid, submitted, remote_addr, is_draft) "." VALUES (%d, %d, %d, %d, '%s', %d)", $node->nid, $sid, $user->uid, time(), $_SERVER['REMOTE_ADDR'], $is_draft);
 
   foreach ($submitted as $cid => $value) {
     // Don't save pagebreaks as submitted data.
@@ -260,7 +275,7 @@
   $query = "SELECT submitted, uid, remote_addr ".
            "FROM {webform_submissions} ".
            "WHERE (( 0 = %d AND remote_addr = '%s') OR uid = %d )".
-           "AND submitted > %d AND nid = %d";
+           "AND submitted > %d AND nid = %d AND is_draft = false";
 
   // Fetch all the entries from the database within the submit interval with this username and IP.
   $result = db_query($query, $user->uid, $_SERVER['REMOTE_ADDR'], $user->uid, ($node->webform['submit_interval'] != -1) ? (time() - $node->webform['submit_interval']) : $node->webform['submit_interval'], $node->nid);
Index: webform.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/webform.install,v
retrieving revision 1.14.2.16.2.16
diff -u -r1.14.2.16.2.16 webform.install
--- webform.install	2 Apr 2008 06:39:52 -0000	1.14.2.16.2.16
+++ webform.install	11 Apr 2008 17:07:56 -0000
@@ -15,7 +15,7 @@
         confirmation text,
         teaser tinyint not null default '0',
         submit_text varchar(255) default NULL,
-        submit_limit tinyint  not null default '-1',
+        submit_limit tinyint not null default '-1',
         submit_interval int not null default '-1',
         email varchar(255) default NULL,
         email_from_name varchar(255) default NULL,
@@ -23,6 +23,7 @@
         email_subject varchar(255) default NULL,
         additional_validate text default NULL,
         additional_submit text default NULL,
+        allow_draft tinyint not null default '0',
         PRIMARY KEY  (nid)
         ) TYPE=MyISAM /*!40100 DEFAULT CHARACTER SET utf8 */"
       );
@@ -48,6 +49,7 @@
         uid int(10) unsigned NOT NULL default '0',
         submitted int(11) NOT NULL default '0',
         remote_addr varchar(128),
+        is_draft tinyint not null default '0',
         PRIMARY KEY (sid),
         UNIQUE KEY sid_nid (sid, nid)
         ) TYPE=MyISAM /*!40100 DEFAULT CHARACTER SET utf8 */"
@@ -80,6 +82,7 @@
         email_subject varchar(255) NOT NULL default '',
         additional_validate text default NULL,
         additional_submit text default NULL,
+        allow_draft smallint NOT NULL default '0',
         PRIMARY KEY (nid)
         )"
       );
@@ -105,6 +108,7 @@
         uid integer NOT NULL default '0',
         submitted integer NOT NULL default '0',
         remote_addr varchar(128) NOT NULL default '',
+        is_draft smallint NOT NULL default '0',
         PRIMARY KEY (sid)
         )"
       );
@@ -672,6 +676,25 @@
 }
 
 /**
+ * Add draft capabilities.
+ */
+function webform_update_21() {
+  $ret = array();
+  switch ($GLOBALS['db_type']) {
+    case 'mysqli':
+    case 'mysql':
+      $ret[] = update_sql("ALTER TABLE {webform_submissions} ADD is_draft tinyint not null default '0'");
+      $ret[] = update_sql("ALTER TABLE {webform} ADD allow_draft tinyint not null default '0'");
+      break;
+    case 'pgsql':
+      $ret[] = update_sql("ALTER TABLE {webform_submissions} ADD is_draft smallint not null default '0'");
+      $ret[] = update_sql("ALTER TABLE {webform} ADD allow_draft smallint not null default '0'");
+      break;
+  }
+  return $ret;
+}
+
+/**
  * Recursively delete all files and folders in the specified filepath, then
  * delete the containing folder.
  *
