Index: /trunk/modules/webform/components/pagebreak.inc
===================================================================
--- /trunk/modules/webform/components/pagebreak.inc (revision 966)
+++ /trunk/modules/webform/components/pagebreak.inc (revision 966)
@@ -0,0 +1,51 @@
+<?php
+
+/** 
+ * function _webform_edit_pagebreak
+ * Create a set of form items to be displayed on the form for editing this component.
+ * Use care naming the form items, as this correlates directly to the database schema.
+ * The component "Name" and "Description" fields are added to every component type and
+ * are not necessary to specify here (although they may be overridden if desired).
+ * @returns An array of form items to be displayed on the edit component page
+ **/
+function _webform_edit_pagebreak($currfield) {
+  $form = array();
+  # no description
+  $form['extra']['description'] = array(
+      '#type' => 'hidden',
+      '#value' => '',
+  );
+  # force the parent to always be root
+  $form['parent'] = array(
+      '#type' => 'hidden',
+      '#value' => '0',
+  );
+  # never mandatory
+  $form['mandatory'] = array(
+      '#type' => 'hidden',
+      '#value' => '',
+  );
+  # weight always 0
+  $form['weight'] = array(
+      '#type' => 'hidden',
+      '#value' => 0,
+  );
+  return $form;
+}
+
+/** 
+ * function _webform_help_textfield
+ * Module specific instance of hook_help
+ **/
+function _webform_help_pagebreak($section) {
+  switch ($section) {
+    case 'admin/settings/webform#pagebreak_description':
+      $output = t("Break up a multi-page form.");
+      break;
+  }
+  return $output;
+}
+
+function _webform_render_pagebreak($currfield) {
+  return '';
+}
Index: /trunk/modules/webform/webform.module
===================================================================
--- /trunk/modules/webform/webform.module (revision 965)
+++ /trunk/modules/webform/webform.module (revision 966)
@@ -757,31 +757,31 @@
   
   // Create our forms and display the output:
   return $form;
 }
 /**
  * Theme the node form. Use a standard Drupal table to organize the components fieldset
  *
  * @param array $form
  * @return string Formatted HTML form, ready for display
  */
 function theme_webform_node_form($form) {
   $node = $form['#node'];
   $rows = array();
   if (is_array($node->webformcomponents) && !empty($node->webformcomponents)) {
     $component_tree = array();
-    _webform_components_tree_build($node->webformcomponents, $component_tree);
+    _webform_components_tree_build($node->webformcomponents, $component_tree, 0, $page_count);
     $component_tree = _webform_components_tree_sort($component_tree);
     // Build the table rows
     function _webform_add_rows($cid, $component, $level, &$form, &$rows) { 
       // Create presentable values
       if (strlen($component['value']) > 30) {
         $component['value'] = htmlspecialchars(substr($component['value'], 0, 30), ENT_QUOTES);
         $component['value'] .= "...";
       }
       
       // Add padding to the radio label
       $form['components']['selected_component'][$cid]['#title'] = '<span style="padding-left: '. ($level * 15) .'px; padding-right: 20px;">'. $form['components']['selected_component'][$cid]['#title'] .'</span>';
       
       // Add each component to a table row
       $rows[] = array(
         drupal_render($form['components']['selected_component'][$cid]),
@@ -1019,31 +1019,31 @@
   if ($preview) {
     $output = preg_replace('/<\/?form[^>]*>/', '', $output);
   }
   
   $node->content['body'] = array('#value' => check_markup($node->body, $node->format, FALSE));
   $node->content['webform'] = array('#value' => $output, '#weight' => 10);
   
   return $node;
 } // end webform_view
 
 /**
  * Client form generation function. If this is displaying an existing
  * submission, pass in the $submission variable with the contents of the
  * submission to be displayed.
  */
-function webform_client_form(&$node, $submission = array()) {
+function webform_client_form(&$node, $submission = array(), $form_values = NULL) {
   global $user;
   
   _webform_load_components(); // Load all the components
   
   if ($_POST['op'] == t('Preview')) {
     $preview = true;
   }
   
   if (module_exists('profile')) {
     profile_load_profile($user);
   }
   
   if ($node->redirect_post && valid_url(trim($node->confirmation), true)) {
     $form['#action'] = trim($node->confirmation);
   }
@@ -1052,92 +1052,149 @@
   $form['#theme'] = 'webform_form_'.$node->nid;
   // Set the encoding type (necessary for file uploads)
   $form['#attributes']['enctype'] = 'multipart/form-data';
   
   if (is_array($node->webformcomponents) && !empty ($node->webformcomponents)) {
     // Prepare a new form array
     $form['submitted'] = array(
       '#tree' => TRUE
     );
     $form['details'] = array(
       '#tree' => true,
     );
     
     // Put the components into a tree structure
     $component_tree = array();
-    _webform_components_tree_build($node->webformcomponents, $component_tree);
+    $page_count = 1;
+    _webform_components_tree_build($node->webformcomponents, $component_tree, 0, $page_count);
+
+    if (!$preview && empty($submission)) {
+      if($page_count > 1) {
+        $next_page = t('Next Page >');
+        $prev_page = t('< Previous Page');
+        if (isset($form_values)) {
+          $page_num = $form_values['details']['page_num'];
+          if ($form_values['op'] == $prev_page && $page_num > 1) {
+            $page_num--;
+          }
+          else if ($form_values['op'] == $next_page && $page_num < $page_count) {
+            $page_num++;
+          }
+        }
+        else {
+          $page_num = 1;
+        }
+  
+        $form['#multistep'] = TRUE;
+        $form['#redirect'] = FALSE;
+  
+        $form['details']['page_num'] = array(
+          '#type'      => 'hidden',
+          '#value'     => $page_num,
+        );
+        // Add the submit button(s)
+        if ($page_num > 1) {
+          $form['submitbutton_prev'] = array(
+            '#type' => 'submit',
+            '#value' => $prev_page,
+            '#weight' => 1000,
+          );
+        }
+        if ($page_num == $page_count) {
+          $form['submitbutton'] = array(
+            '#type' => 'submit',
+            '#value' => t('Submit'),
+            '#weight' => 1001,
+          );
+        }
+        else if ($page_num < $page_count) {
+          $form['submitbutton_next'] = array(
+            '#type' => 'submit',
+            '#value' => $next_page,
+            '#weight' => 1001,
+          );
+        }
+      }
+      else {
+        $page_num = 1;
+        // Add the submit button
+        $form['submitbutton'] = array(
+          '#type' => 'submit',
+          '#value' => t('Submit'),
+          '#weight' => 1000,
+        );
+      }
+    }
+
     // Recursively add components to the form
+    $session_key = 'webform_form_'.$node->nid;
     foreach ($component_tree['children'] as $cid => $component) {
-      _webform_client_form_add_component($cid, $component, $form['submitted'], $form, $submission);
+      if (is_array($_SESSION[$session_key]) && isset($_SESSION[$session_key][$cid])) {
+        $component['value'] = $_SESSION[$session_key][$cid];
+      }
+      _webform_client_form_add_component($cid, $component, $form['submitted'], $form, $submission, $page_num);
     }
     // Do not display the submit button if this is a preview or submission view
     if (!$preview && empty($submission)) {
       // Additional hidden elements   
       // Email subject and sender
       $form['details']['email_subject'] = array(
         '#type'      => 'hidden',
         '#value'     => $node->email_subject,
       );
       $form['details']['email_from_name'] = array(
         '#type'      => 'hidden',
         '#value'     => $node->email_from_name,
       );
       $form['details']['email_from_address'] = array(
         '#type'      => 'hidden',
         '#value'     => $node->email_from_address,
       );
       $form['details']['nid'] = array(
         '#type'      => 'value',
         '#value'     => $node->nid,
       );
-      
-      // Add the submit button
-      $form['submitbutton'] = array(
-        '#type' => 'submit',
-        '#value' => t('Submit'),
-        '#weight' => 1000,
-      );
     }
   }
   
   return $form;
 }
 
-function _webform_client_form_add_component($cid, $component, &$parent_fieldset, &$form, $submission) {
+function _webform_client_form_add_component($cid, $component, &$parent_fieldset, &$form, $submission, $page_num) {
   // Load with submission information if necessary
   if (!empty($submission)) {
     // This component is display only, with the value set according information
     // previously submitted in the submission numbered $sid_to_display
     $display_function = "_webform_submission_display_". $component['type'];
     if (function_exists($display_function)) {
       $parent_fieldset[$cid] = $display_function($submission['data'][$cid], $component);
     }
   }
-  else {
+  else if ($component['page_num'] == $page_num) {
     // Add this user-defined field to the form (with all the values that are always available)      
     $render_function = "_webform_render_". $component['type'];
     if (function_exists($render_function)) {
       $parent_fieldset[$cid] = $render_function($component); // Call the component render function
     }
     else {
       drupal_set_message(t("The webform component") ." '". $component['type'] ."' ". t("is not able to be displayed"));
     }
   }
   
   if (is_array($component['children'])) {
     foreach ($component['children'] as $scid => $subcomponent) {
-      _webform_client_form_add_component($scid, $subcomponent, $parent_fieldset[$cid], $form, $submission);
+      _webform_client_form_add_component($scid, $subcomponent, $parent_fieldset[$cid], $form, $submission, $page_num);
     }
   }
 }
 
 function webform_client_form_validate($form_id, $form_values) {
   global $user, $base_url;
   include_once(drupal_get_path('module', 'webform') . "/webform.inc");
   $node = node_load(array('nid' => $form_values['details']['nid']));
 
   // Flatten trees within the submission
   $form_values['submitted_tree'] = $form_values['submitted'];
   _webform_client_form_submit_flatten($node, $form_values['submitted'], $form_values['submitted']);
   
   // Verify that this submission is within the submission limits on this form
   if ($violation_count = _webform_submission_limit_check ($node, $form_values)) {
@@ -1162,34 +1219,49 @@
       }
     }
     form_set_error('', t("You have submitted the maximum number of entries. Check submission guidelines."));
   }
   
   if (trim($node->additional_validate)) {
     // We use eval here (rather than drupal_eval) because the user needs access to local variables
     eval("?>".$node->additional_validate);
   }
 }
 
 function webform_client_form_submit($form_id, $form_values) {
   global $user, $base_url;
   include_once (drupal_get_path('module', 'webform')."/webform.inc");
   $node = node_load(array('nid'=>$form_values['details']['nid']));
+  $session_key = 'webform_form_'.$node->nid;
+
+  if ($form_values['op'] != t('Submit')) {
+    // This is a multi-page form that is not yet complete
+    _webform_components_save_values($form_values['submitted'], $session_key);
+    return;
+  }
 
   // Flatten trees within the submission
   $form_values['submitted_tree'] = $form_values['submitted'];
   _webform_client_form_submit_flatten($node, $form_values['submitted'], $form_values['submitted']);
+
+  if (is_array($_SESSION[$session_key])) {
+    // Merge any submission data stored in $_SESSION for multistep forms
+    foreach ($_SESSION[$session_key] as $key => $val) {
+      $form_values['submitted'][$key] = $val;
+    }
+    unset($_SESSION[$session_key]);
+  }
   
   // Process post-submission data if necessary in the components
   _webform_load_components();
   foreach ($node->webformcomponents as $cid => $component) {
     $submit_function = "_webform_submit_".$component['type'];
     if (function_exists($submit_function)) {
       $submit_function($form_values['submitted'][$cid], $component); // Call the component process submission function
     }
   }
 
   // Perform additional submit processing
   if (trim($node->additional_submit)) {
     // We use eval here (rather than drupal_eval) because the user needs access to local variables
     eval("?>".$node->additional_submit);
   }
@@ -1466,38 +1538,56 @@
 
 function _webform_components_decode($components) {
   if (is_array($components)) {
     foreach ($components as $cid => $value) {
       if (is_string($value)) {
         $components[$cid] = unserialize(base64_decode($value));
       }
     }
   }
   return $components;
 }
 
 /**
  * Convert an array of components into a tree
  */
-function _webform_components_tree_build($src, &$tree, $parent = 0) {
+function _webform_components_tree_build($src, &$tree, $parent, &$page_count) {
   foreach ($src as $cid => $component) {
     if ($component['parent'] == $parent) {
-      _webform_components_tree_build($src, $component, $cid);
+      _webform_components_tree_build($src, $component, $cid, $page_count);
       $tree['children'][$cid] = $component;
+      $tree['children'][$cid]['page_num'] = $page_count;
+      if ($component['type'] == 'pagebreak') {
+        $page_count++;
+      }
     }
   }
   return $tree;
+}
+
+/**
+ * Store values from an intermediate stage of a multistep form in $_SESSION
+ */
+function _webform_components_save_values($form_values, $session_key) {
+  foreach ($form_values as $key => $val) {
+    if (is_array($val)) {
+      _webform_components_save_values($val, $session_key);
+    }
+    else {
+      $_SESSION[$session_key][$key] = $val;
+    }
+  }
 }
 
 /**
  * Helper for the uasort in webform_tree_sort()
  */
 function _webform_components_sort($a, $b) {
   if ($a['weight'] == $b['weight']) {
     return strcasecmp($a['name'], $b['name']);
   }
   return ($a['weight'] < $b['weight']) ? -1 : 1;
 }
 
 
 /**
  * Sort each level of a component tree by weight and name
