Index: webform_hooks.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/webform_hooks.php,v
retrieving revision 1.6
diff -u -r1.6 webform_hooks.php
--- webform_hooks.php	25 Jan 2010 04:48:50 -0000	1.6
+++ webform_hooks.php	7 Feb 2010 01:35:10 -0000
@@ -7,7 +7,7 @@
  */
 
 /**
- * @defgroup webform_component Sample Webform Component
+ * @defgroup webform_hooks Webform Module Hooks
  * @{
  * Webform's hooks enable other modules to intercept events within Webform, such
  * as the completion of a submission or adding validation. Webform's hooks also
@@ -15,6 +15,80 @@
  */
 
 /**
+ * Respond to the loading of Webform submissions.
+ *
+ * @param $submissions
+ *   An array of Webform submissions that are being loaded, keyed by the
+ *   submission ID. Modifications to the submissions are done by reference.
+ */
+function hook_webform_submission_load(&$submissions) {
+  foreach ($submissions as $sid => $submission) {
+    $submissions[$sid]->new_property = 'foo';
+  }
+}
+
+/**
+ * Modify a Webform submission, prior to saving it in the database.
+ *
+ * @param $node
+ *   The Webform node on which this submission was made.
+ * @param $submission
+ *   The Webform submission that is about to be saved to the database.
+ */
+function hook_webform_submission_presave($node, &$submission) {
+  // Update some component's value before it is saved.
+  $component_id = 4;
+  $submission->data[$component_id]['value'][0] = 'foo';
+}
+
+/**
+ * Respond to a Webform submission being inserted.
+ *
+ * Note that this hook is called after a submission has already been saved to
+ * the database. If needing to modify the submission prior to insertion, use
+ * hook_webform_submission_presave().
+ *
+ * @param $node
+ *   The Webform node on which this submission was made.
+ * @param $submission
+ *   The Webform submission that was just inserted into the database.
+ */
+function hook_webform_submission_insert($node, $submission) {
+  // Insert a record into a 3rd-party module table when a submission is added.
+  db_query("INSERT INTO {mymodule_table} nid = %d, sid = %d, foo = '%s'", $node->nid, $submission->sid, 'foo_data');
+}
+
+/**
+ * Respond to a Webform submission being updated.
+ *
+ * Note that this hook is called after a submission has already been saved to
+ * the database. If needing to modify the submission prior to updating, use
+ * hook_webform_submission_presave().
+ *
+ * @param $node
+ *   The Webform node on which this submission was made.
+ * @param $submission
+ *   The Webform submission that was just updated in the database.
+ */
+function hook_webform_submission_update($node, $submission) {
+  // Update a record in a 3rd-party module table when a submission is updated.
+  db_query("UPDATE {mymodule_table} SET (foo) VALUES ('%s') WHERE nid = %d, sid = %d", 'foo_data', $node->nid, $submission->sid);
+}
+
+/**
+ * Respond to a Webform submission being deleted.
+ *
+ * @param $node
+ *   The Webform node on which this submission was made.
+ * @param $submission
+ *   The Webform submission that was just deleted from the database.
+ */
+function hook_webform_submission_delete($node, $submission) {
+  // Delete a record from a 3rd-party module table when a submission is deleted.
+  db_query("DELETE FROM {mymodule_table} WHERE nid = %d, sid = %d", $node->nid, $submission->sid);
+}
+
+/**
  * Define components to Webform.
  *
  * @return
Index: webform.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/webform.module,v
retrieving revision 1.167
diff -u -r1.167 webform.module
--- webform.module	26 Jan 2010 03:57:04 -0000	1.167
+++ webform.module	7 Feb 2010 01:35:10 -0000
@@ -1631,10 +1631,20 @@
   // Check if user is submitting as a draft.
   $is_draft = $form_state['values']['op'] == $draft_op;
 
+  // Create a submission object.
+  $submission = (object) array(
+    'nid' => $node->nid,
+    'uid' => $user->uid,
+    'submitted' => time(),
+    'remote_addr' => ip_address(),
+    'is_draft' => $is_draft,
+    'data' => webform_submission_data($node, $form_state['values']['submitted']),
+  );
+
   // Save the submission to the database.
   if (empty($form_state['values']['details']['sid'])) {
     // No sid was found thus insert it in the dataabase.
-    $form_state['values']['details']['sid'] = webform_submission_insert($node, $form_state['values']['submitted'], $is_draft);
+    $form_state['values']['details']['sid'] = webform_submission_insert($node, $submission);
     $form_state['values']['details']['is_new'] = TRUE;
 
     // Set a cookie including the server's submission time.
@@ -1653,7 +1663,8 @@
   }
   else {
     // Sid was found thus update the existing sid in the database.
-    webform_submission_update($node, $form_state['values']['details']['sid'], $form_state['values']['submitted'], $is_draft);
+    $submission->sid = $form_state['values']['details']['sid'];
+    webform_submission_update($node, $submission);
     $form_state['values']['details']['is_new'] = FALSE;
   }
 
@@ -2177,6 +2188,13 @@
 }
 
 /**
+ * Filter labels for display by running through XSS checks.
+ */
+function _webform_filter_xss($string) {
+  return filter_xss($string, array('a', 'em', 'strong', 'code'));
+}
+
+/**
  * Given a form_key and a list of form_key parents, determine the cid.
  *
  * @param $node
Index: includes/webform.submissions.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/includes/webform.submissions.inc,v
retrieving revision 1.10
diff -u -r1.10 webform.submissions.inc
--- includes/webform.submissions.inc	23 Jan 2010 02:10:06 -0000	1.10
+++ includes/webform.submissions.inc	7 Feb 2010 01:35:10 -0000
@@ -9,65 +9,88 @@
  * @author Nathan Haug <nate@lullabot.com>
  */
 
-function webform_submission_update($node, $sid, $submitted, $is_draft = 0) {
-  // 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_keys($submitted);
-    db_query("DELETE FROM {webform_submitted_data} WHERE sid = %d AND cid IN (" . implode(', ', $submitted_cids) . ")", $sid);
-  }
-  else {
-    db_query("UPDATE {webform_submissions} SET is_draft = 0");
-    db_query("DELETE FROM {webform_submitted_data} WHERE sid = %d", $sid);
-  }
+/**
+ * Given an array of submitted values, flatten it into data for a submission.
+ */
+function webform_submission_data($node, $submitted) {
+  $data = array();
 
-  // Then re-add it to the database.
-  foreach ($submitted as $cid => $value) {
+  foreach ($submitted as $cid => $values) {
     // Don't save pagebreaks as submitted data.
     if ($node->webform['components'][$cid]['type'] == 'pagebreak') {
       continue;
     }
 
-    if (is_array($value)) {
+    if (is_array($values)) {
       $delta = 0;
-      foreach ($value as $k => $v) {
-        db_query("INSERT INTO {webform_submitted_data} (nid, sid, cid, no, data) VALUES (%d, %d, %d, %d, '%s')", $node->nid, $sid, $cid, $delta, $v);
+      foreach ($values as $key => $value) {
+        $data[$cid]['value'][$delta] = $value;
         $delta++;
       }
     }
     else {
-      db_query("INSERT INTO {webform_submitted_data} (nid, sid, cid, no, data) VALUES (%d, %d, %d, %d, '%s')", $node->nid, $sid, $cid, 0, $value);
+      $data[$cid]['value'][0] = $values;
     }
   }
 
-  return $sid;
+  return $data;
 }
 
-function webform_submission_insert($node, $submitted, $is_draft = 0) {
-  global $user;
+function webform_submission_update($node, $submission) {
+  // Allow other modules to modify the submission before saving.
+  foreach (module_implements('webform_submission_presave') as $module) {
+    $function = $module . '_webform_submission_presave';
+    $function($node, $submission);
+  }
 
-  $result = db_query("INSERT INTO {webform_submissions} (nid, uid, submitted, remote_addr, is_draft) VALUES (%d, %d, %d, '%s', %d)", $node->nid, $user->uid, time(), ip_address(), $is_draft);
-  $sid = db_last_insert_id('webform_submissions', 'sid');
+  // Update the main submission info.
+   $result = db_query("UPDATE {webform_submissions} SET uid = %d, submitted = %d, remote_addr = %d, is_draft = %d WHERE sid = %d", $submission->uid, $submission->submitted, $submission->remote_addr, $submission->is_draft, $submission->sid);
 
-  foreach ($submitted as $cid => $value) {
-    // Don't save pagebreaks as submitted data.
-    if ($node->webform['components'][$cid]['type'] == 'pagebreak') {
-      continue;
-    }
+  // If is draft, only delete data for components submitted, to
+  // 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);
+  }
+  else {
+    db_query("DELETE FROM {webform_submitted_data} WHERE sid = %d", $submission->sid);
+  }
 
-    if (is_array($value)) {
-      $delta = 0;
-      foreach ($value as $k => $v) {
-        db_query("INSERT INTO {webform_submitted_data} (nid, sid, cid, no, data) VALUES (%d, %d, %d, %d, '%s')", $node->nid, $sid, $cid, $delta, $v);
-        $delta++;
-      }
+  // Then re-add submission data to the database.
+  $submission->is_new = FALSE;
+  webform_submission_insert($node, $submission);
+
+  module_invoke_all('webform_submission_update', $node, $submission);
+
+  return $submission->sid;
+}
+
+function webform_submission_insert($node, $submission) {
+  // The submission ID may already be set if being called as an update.
+  if (!isset($submission->sid) && (!isset($submission->is_new) || $submission->is_new == FALSE)) {
+    // Allow other modules to modify the submission before saving.
+    foreach (module_implements('webform_submission_presave') as $module) {
+      $function = $module . '_webform_submission_presave';
+      $function($node, $submission);
     }
-    else {
-      db_query("INSERT INTO {webform_submitted_data} (nid, sid, cid, no, data) VALUES (%d, %d, %d, %d, '%s')", $node->nid, $sid, $cid, 0, $value);
+
+    $result = db_query("INSERT INTO {webform_submissions} (nid, uid, submitted, remote_addr, is_draft) VALUES (%d, %d, %d, '%s', %d)", $node->nid, $submission->uid, $submission->submitted, $submission->remote_addr, $submission->is_draft);
+    $submission->sid = db_last_insert_id('webform_submissions', 'sid');
+    $is_new = TRUE;
+  }
+
+  foreach ($submission->data as $cid => $values) {
+    foreach ($values['value'] as $delta => $value) {
+      db_query("INSERT INTO {webform_submitted_data} (nid, sid, cid, no, data) VALUES (%d, %d, %d, %d, '%s')", $node->nid, $submission->sid, $cid, $delta, $value);
     }
   }
 
-  return $sid;
+  // Invoke the insert hook after saving all the data.
+  if (isset($is_new)) {
+    module_invoke_all('webform_submission_insert', $node, $submission);
+  }
+
+  return $submission->sid;
 }
 
 /**
@@ -93,6 +116,8 @@
 
   db_query('DELETE FROM {webform_submitted_data} WHERE nid = %d AND sid = %d', $node->nid, $submission->sid);
   db_query('DELETE FROM {webform_submissions} WHERE nid = %d AND sid = %d', $node->nid, $submission->sid);
+
+  module_invoke_all('webform_submission_delete', $node, $submission);
 }
 
 /**
@@ -195,52 +220,66 @@
 /**
  * Return all the submissions for a particular node.
  *
- * @param $nid
- *   The node ID for which submissions are being fetched.
+ * @param $filters
+ *   An array of filters to apply to this query. Usually in the format
+ *   array('nid' => $nid, 'uid' => $uid). A single integer may also be passed
+ *   in, which will be equivalent to specifying a $nid filter.
  * @param $header
  *   If the results of this fetch will be used in a sortable
  *   table, pass the array header of the table.
- * @param $uid
- *   Optional; the user ID to filter the submissions by.
- * @return $submissions
- *   An array of submissions matching your filters.
+ * @param $pager_count
+ *   Optional. The number of submissions to include in the results.
  */
-function webform_get_submissions($nid, $header = NULL, $uid = NULL, $pager_count = 0) {
-  $sids = NULL;
-  $arguments = array($nid);
-  if ($pager_count || $uid === 0) {
-    $pager_query = 'SELECT * FROM {webform_submissions} WHERE nid = %d';
-    if ($uid !== NULL) {
-      $pager_query .= ' AND uid = %d';
-      $arguments[] = $uid;
+function webform_get_submissions($filters = array(), $header = NULL, $pager_count = 0) {
+  $submissions = array();
+
+  if (!is_array($filters)) {
+    $filters = array('nid' => $filters);
+  }
+
+  // No need to find SIDs if it was given to us.
+  if (isset($filters['sid'])) {
+    $sids = array($filters['sid']);
+  }
+  // Build the list of SIDs that need to be retrieved.
+  else {
+    $arguments = array_values($filters);
+    $where = array();
+    foreach ($filters as $column => $value) {
+      $where[] = $column . ' = ' . (is_numeric($value) ? '%d' : "'%s'");
     }
-    if ($uid === 0) {
-      $submissions = array_keys($_SESSION['webform_submission']);
-      $placeholders = count($submissions) ? array_fill(0, count($submissions), "%d") : array();
-      $pager_query .= count($submissions) ? ' AND sid IN (' . implode(',', $placeholders) . ')' : ' AND sid = 0';
-      $arguments = array_merge($arguments, $submissions);
+
+    if (isset($filters['uid']) && $filters['uid'] === 0) {
+      $anonymous_sids = array_keys($_SESSION['webform_submission']);
+      if (count($anonymous_sids)) {
+        $placeholders = array_fill(0, count($anonymous_sids), "%d");
+        $where[] = 'sid IN (' . implode(',', $placeholders) . ')';
+        $arguments = array_merge($arguments, $anonymous_sids);
+      }
     }
+
+    $where_clause = implode(' AND ', $where);
+    $pager_query = "SELECT sid FROM {webform_submissions} WHERE $where_clause";
+
     $result = pager_query($pager_query, $pager_count, 0, NULL, $arguments);
     $sids = array();
     while ($row = db_fetch_object($result)) {
       $sids[] = $row->sid;
+      $submissions[$row->sid] = FALSE;
     }
-    $sids = implode($sids, ',');
   }
 
+  // If there are no submissions being retrieved, return an empty array.
+  if (empty($sids)) {
+    return $submissions;
+  }
+
+  // Query the required submission data.
   $query = 'SELECT s.*, sd.cid, sd.no, sd.data, u.name, u.mail, u.status '.
            'FROM {webform_submissions} s '.
            'LEFT JOIN {webform_submitted_data} sd ON sd.sid = s.sid '.
            'LEFT JOIN {users} u ON u.uid = s.uid '.
-           'WHERE sd.nid = %d';
-
-  if ($pager_count && isset($sids)) {
-    $query .= empty($sids) ? ' AND s.sid = 0' : ' AND s.sid IN (%s)';
-  }
-
-  if ($uid) {
-    $query .= ' AND u.uid = %d';
-  }
+           'WHERE s.sid IN (' . implode($sids, ',') . ')';
 
   if (is_array($header)) {
     $query .= tablesort_sql($header);
@@ -252,16 +291,15 @@
   else {
     $query .= ' ORDER BY sid ASC, cid ASC, no ASC';
   }
+  $result = db_query($query);
 
-  $res = $pager_count ? db_query($query, $nid, $sids, $uid) : db_query($query, $nid, $uid);
-  $submissions = array();
+  // Convert the queried rows into submissions.
   $previous = array();
-
-  // Outer loop: iterate for each submission.
-  while ($row = db_fetch_object($res)) {
+  while ($row = db_fetch_object($result)) {
     if ($row->sid != $previous) {
       $submissions[$row->sid] = new stdClass();
       $submissions[$row->sid]->sid = $row->sid;
+      $submissions[$row->sid]->nid = $row->nid;
       $submissions[$row->sid]->submitted = $row->submitted;
       $submissions[$row->sid]->remote_addr = $row->remote_addr;
       $submissions[$row->sid]->uid = $row->uid;
@@ -273,6 +311,11 @@
     $previous = $row->sid;
   }
 
+  foreach (module_implements('webform_submission_load') as $module) {
+    $function = $module . '_webform_submission_load';
+    $function($submissions);
+  }
+
   return $submissions;
 }
 
@@ -314,31 +357,25 @@
 function webform_get_submission($nid, $sid, $reset = FALSE) {
   static $submissions = array();
 
-  if (!isset($submissions[$nid][$sid]) || $reset) {
-    $query = 'SELECT s.*, sd.cid, sd.no, sd.data '.
-             'FROM {webform_submitted_data} as sd '.
-             'LEFT JOIN {webform_submissions} as s on (sd.sid = s.sid) '.
-             'WHERE sd.sid = %d AND s.nid = %d';
-
-    $result = db_query($query, $sid, $nid);
-    if ($row = db_fetch_object($result)) {
-      $submission = new stdClass;
-      $submission->nid = $row->nid;
-      $submission->sid = $row->sid;
-      $submission->uid = $row->uid;
-      $submission->remote_addr = $row->remote_addr;
-      $submission->submitted = $row->submitted;
-      $submission->is_draft = $row->is_draft;
-
-      while ($row) {
-        $submission->data[$row->cid]['value'][$row->no] = $row->data;
-        $row = db_fetch_object($result);
-      }
+  if ($reset) {
+    $submissions = array();
+    if (!isset($sid)) {
+      return;
     }
-    $submissions[$nid][$sid] = isset($submission) ? $submission : NULL;
   }
 
-  return $submissions[$nid][$sid];
+  // Load the submission if needed.
+  if (!isset($submissions[$sid])) {
+    $new_submissions = webform_get_submissions(array('sid' => $sid));
+    $submissions[$sid] = $new_submissions[$sid];
+  }
+
+  // Ensure that the requested NID matches the submission NID.
+  if ($submissions[$sid]->nid != $nid) {
+    return FALSE;
+  }
+
+  return $submissions[$sid];
 }
 
 function _webform_submission_spam_check($to, $subject, $from, $headers = array()) {
Index: includes/webform.report.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/includes/webform.report.inc,v
retrieving revision 1.15
diff -u -r1.15 webform.report.inc
--- includes/webform.report.inc	25 Jan 2010 04:48:51 -0000	1.15
+++ includes/webform.report.inc	7 Feb 2010 01:35:10 -0000
@@ -33,11 +33,11 @@
       webform_disable_page_cache();
     }
     webform_set_breadcrumb($node);
-    $submissions = webform_get_submissions($node->nid, $header, $user->uid, $pager_count);
+    $submissions = webform_get_submissions(array('nid' => $node->nid, 'uid' => $user->uid), $header, $pager_count);
     $count = webform_get_submission_count($node->nid, $user->uid);
   }
   else {
-    $submissions = webform_get_submissions($node->nid, $header, NULL, $pager_count);
+    $submissions = webform_get_submissions($node->nid, $header, $pager_count);
     $count = webform_get_submission_count($node->nid);
   }
   return theme('webform_results_submissions', $node, $submissions, $count, $pager_count);
@@ -193,7 +193,7 @@
 
   // Get all the submissions for the node.
   $header = theme('webform_results_table_header', $node);
-  $submissions = webform_get_submissions($node->nid, $header, NULL, $pager_count);
+  $submissions = webform_get_submissions($node->nid, $header, $pager_count);
   $total_count = webform_get_submission_count($node->nid);
 
   $output = theme('webform_results_table', $node, $node->webform['components'], $submissions, $total_count, $pager_count);
