diff --git a/README.txt b/README.txt
index 7adb34a..ced398c 100644
--- a/README.txt
+++ b/README.txt
@@ -1,25 +1,28 @@
 Description
 -----------
 This module adds a webform content type to your Drupal site.
-A webform can be a questionnaire, contact or request form. These can be used 
+A webform can be a questionnaire, contact or request form. These can be used
 by visitor to make contact or to enable a more complex survey than polls
-provide. Submissions from a webform are saved in a database table and 
+provide. Submissions from a webform are saved in a database table and
 can optionally be mailed to e-mail addresses upon submission.
 
 Requirements
 ------------
 Drupal 7.x
+Token
 
 Installation
 ------------
-1. Copy the entire webform directory the Drupal sites/all/modules directory.
+1. You must also install the Token module (http://www.drupal.org/project/token) to use Webform.
 
-2. Login as an administrator. Enable the module in the "Administer" -> "Modules"
+2. Copy the entire webform directory the Drupal sites/all/modules directory.
 
-3. (Optional) Edit the settings under "Administer" -> "Configuration" ->
+3. Login as an administrator. Enable the module in the "Administer" -> "Modules"
+
+4. (Optional) Edit the settings under "Administer" -> "Configuration" ->
    "Content authoring" -> "Webform settings"
 
-4. Create a webform node at node/add/webform.
+5. Create a webform node at node/add/webform.
 
 Upgrading from previous versions
 --------------------------------
@@ -28,11 +31,13 @@ have been running Webform 3.x on your Drupal 6 site before upgrading to Drupal
 7 and Webform 3.x. You cannot upgrade directly from Webform 6.x-2.x to Webform
 7.x-3.x.
 
-1. Copy the entire webform directory the Drupal modules directory.
+1. Install the Token module if it's not already installed.
+
+2. Copy the entire webform directory the Drupal modules directory.
 
-2. Login as the FIRST user or change the $access_check in update.php to FALSE
+3. Login as the FIRST user or change the $access_check in update.php to FALSE
 
-3. Run update.php (at http://www.example.com/update.php)
+4. Run update.php (at http://www.example.com/update.php)
 
 Support
 -------
diff --git a/includes/webform.emails.inc b/includes/webform.emails.inc
index 4c44a01..61fc952 100644
--- a/includes/webform.emails.inc
+++ b/includes/webform.emails.inc
@@ -284,7 +284,8 @@ function webform_email_edit_form($form, $form_state, $node, $email = array()) {
   );
 
   $form['template']['tokens'] = array(
-    '#markup' => theme('webform_token_help', array('groups' => 'all')),
+    '#theme' => 'token_tree',
+    '#token_types' => array('webform-submission'),
   );
 
   $form['template']['components'] = array(
diff --git a/webform.info b/webform.info
index 149b887..e60f054 100644
--- a/webform.info
+++ b/webform.info
@@ -5,6 +5,8 @@ core = 7.x
 package = Webform
 configure = admin/config/content/webform
 
+dependencies[] = token
+
 ; Files that contain classes:
 files[] = includes/webform.export.inc
 
diff --git a/webform.install b/webform.install
index 19dacf4..a946562 100644
--- a/webform.install
+++ b/webform.install
@@ -831,3 +831,87 @@ function webform_update_7319(&$sandbox) {
 function webform_update_7320() {
   db_query("UPDATE {file_managed} SET status = 1 WHERE fid IN (SELECT fid FROM {file_usage} WHERE module = :module_name)", array(':module_name' => 'webform'));
 }
+
+/**
+ * Rewrite token replacement system to use D7 tokens.
+ * Please download and install the Token module from drupal.org.
+ * Otherwise some tokens will not be rendered. 
+ */
+function webform_update_7321() {
+  if (!module_enable(array('token'))) {
+    drupal_set_message('warning', t('Your existing webforms have been upgraded to the Drupal 7 Token system. Please download and install the <a href="http://drupal.org/project/token" target="_blank">Token module</a>. Otherwise some tokens will not be rendered.'));
+  }
+  $patterns = array(
+    '%username',
+    '%useremail',
+    '%uid',
+    '%date',
+    '%ip_address',
+    '%site',
+    '%nid',
+    '%title',
+    '%email_values',
+    '%submission_url',
+    '%sid',
+  );
+  $dpatterns = array(
+    '/%get\[([^\]]+)\]/m',
+    '/%email\[([^\]]+)\]\[([^\]]+)\]/m',
+    '/%email\[([^\]]+)\]/m',
+    '/%value\[([^\]]+)\]\[([^\]]+)\]/m',
+    '/%value\[([^\]]+)\]/m',
+  );
+  $replacements = array(
+    '[current-user:name]',
+    '[current-user:mail]',
+    '[current-user:uid]',
+    '[current-date:long]',
+    '[current-user:ip-address]',
+    '[site:name]',
+    '[node:nid]',
+    '[node:title]',
+    '[webform-submission:email-values]',
+    '[webform-submission:url]',
+    '[webform-submission:sid]',
+  );
+  $dreplacements = array(
+    '[current-page:query:$1]',
+    '[webform-submission:email:$2]',
+    '[webform-submission:email:$1]',
+    '[webform-submission:submission:$2]',
+    '[webform-submission:submission:$1]',
+  );
+  $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('wc')
+    ->execute();
+  foreach ($result as $component) {
+    $original_extra = $component['extra'];
+    $original_value = $component['value'];
+    $component['extra'] = unserialize($component['extra']);
+    if (isset($component['extra']['description'])) {
+      $description = str_replace($patterns, $replacements, $component['extra']['description']);
+      $description = preg_replace($dpatterns, $dreplacements, $description);
+      $component['extra']['description'] = $description;
+    }
+    $component['extra'] = serialize($component['extra']);
+    $value = str_replace($patterns, $replacements, $component['value']);
+    $value = preg_replace($dpatterns, $dreplacements, $value);
+    $component['value'] = $value;
+    if ($component['extra'] != $original_extra || $component['value'] != $original_value) {
+      drupal_write_record('webform_component', $component, array('nid', 'cid'));
+    }
+  }
+
+  $result = db_select('webform_emails', 'we', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('we')
+    ->execute();
+  foreach ($result as $email) {
+    $original = $email['template'];
+    $template = str_replace($patterns, $replacements, $email['template']);
+    $template = preg_replace($dpatterns, $dreplacements, $template);
+    if ($template != $original) {
+      $email['template'] = $template;
+      drupal_write_record('webform_emails', $email, array('nid', 'eid'));
+    }
+  }
+}
diff --git a/webform.module b/webform.module
index 2fffd0c..90efe05 100644
--- a/webform.module
+++ b/webform.module
@@ -2872,6 +2872,21 @@ function _webform_filter_values($string, $node = NULL, $submission = NULL, $emai
     return $string;
   }
 
+  //First, let the token api have at it
+  $token_data = array(
+    'user' => $user,
+  );
+  if ($node) {
+    $token_data['node'] = $node;
+  }
+  if ($submission) {
+    $token_data['webform-submission'] = $submission;
+  }
+  if ($email) {
+    $token_data['webform-email'] = $email;
+  }
+  $string = token_replace($string, $token_data);
+
   // Setup default token replacements.
   if (!isset($replacements)) {
     $replacements['unsafe'] = array();
@@ -3216,10 +3231,15 @@ function theme_webform_token_help($variables) {
     '#type' => 'fieldset',
     '#collapsible' => TRUE,
     '#collapsed' => TRUE,
-    '#children' => '<div>' . $output . '</div>',
     '#attributes' => array('class' => array('collapsible', 'collapsed')),
   );
-  return theme('fieldset', array('element' => $fieldset));
+
+  $fieldset['token_tree'] = array(
+    '#theme' => 'token_tree',
+    '#token_types' => array('node'),
+  );
+
+  return render($fieldset);
 }
 
 function _webform_safe_name($name) {
diff --git a/webform.tokens.inc b/webform.tokens.inc
new file mode 100644
index 0000000..7509c55
--- /dev/null
+++ b/webform.tokens.inc
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * @file
+ * Builds placeholder replacement tokens for webform-related data.
+ */
+
+/**
+ * Implements hook_token_info().
+ */
+function webform_token_info() {
+  // Webform submission tokens.
+  $info['types']['webform-submission'] = array(
+    'name' => t('Webform node'),
+    'description' => t('Tokens related to webform submissions.'),
+    'needs-data' => 'webform-submission',
+  );
+  $info['tokens']['webform-submission']['sid'] = array(
+    'name' => t('Submission ID'),
+    'description' => t('The unique indentifier for the webform submission.'),
+  );
+  $info['tokens']['webform-submission']['node'] = array(
+    'name' => t('Node'),
+    'description' => t('The webform content that the submission came from.'),
+    'type' => 'node',
+  );
+  $info['tokens']['webform-submission']['date'] = array(
+    'name' => t('Date submitted'),
+    'description' => t('The date the webform was submitted.'),
+    'type' => 'date',
+  );
+  $info['tokens']['webform-submission']['ip-address'] = array(
+    'name' => t('IP address'),
+    'description' => t('The IP address that was used when submitting the webform.'),
+  );
+  $info['tokens']['webform-submission']['user'] = array(
+    'name' => t('Submitter'),
+    'description' => t('The user that submitted the webform result.'),
+    'type' => 'user',
+  );
+  $info['tokens']['webform-submission']['url'] = array(
+    'name' => t('URL'),
+    'description' => t('Webform tokens related to URLs.'),
+    'type' => 'url',
+  );
+  $info['tokens']['webform-submission']['email-values'] = array(
+    'name' => t('Webform submission values'),
+    'description' => t('All included components in a hierarchical structure.'),
+  );
+  $info['tokens']['webform-submission']['email'] = array(
+    'name' => t('Webform label and value'),
+    'description' => t('A formatted value and field label.'),
+    'dynamic' => TRUE,
+  );
+  $info['tokens']['webform-submission']['submission'] = array(
+    'name' => t('Webform submission component value'),
+    'description' => t('Webform tokens from submitted data. Replace the "?" with the "Field Key" of the component that contains the appropriate values.'),
+    'dynamic' => TRUE,
+  );
+
+  return $info;
+}
+
+/**
+ * Implements hook_tokens().
+ */
+function webform_tokens($type, $tokens, array $data = array(), array $options = array()) {
+  $replacements = array();
+
+  $url_options = array('absolute' => TRUE);
+  if (isset($options['language'])) {
+    $url_options['language'] = $options['language'];
+    $language_code = $options['language']->language;
+  }
+  else {
+    $language_code = NULL;
+  }
+
+  $sanitize = !empty($options['sanitize']);
+
+  // Webform tokens  (caching globally)
+  if ($type == 'webform-submission' && !empty($data['webform-submission'])) {
+    $submission = $data['webform-submission'];
+
+    foreach ($tokens as $name => $original) {
+      switch ($name) {
+        case 'sid':
+          $replacements[$original] = $submission->sid;
+          break;
+        case 'node':
+          $node = node_load($submission->nid);
+          $replacements[$original] = $sanitize ? check_plain($node->title) : $node->title;
+          break;
+        case 'date':
+          $replacements[$original] = format_date($submission->submitted, 'medium', '', NULL, $language_code);
+          break;
+        case 'ip-address':
+          $replacements[$original] = $sanitize ? check_plain($submission->remote_addr) : $submission->remote_addr;
+          break;
+        case 'user':
+          $account = user_load($submission->uid);
+          $name = format_username($account);
+          $replacements[$original] = $sanitize ? check_plain($name) : $name;
+          break;
+        case 'url':
+          $replacements[$original] = url("node/{$submission->nid}/submission/{$submission->sid}", $url_options);
+          break;
+        case 'edit-url':
+          $replacements[$original] = url("node/{$submission->nid}/submission/{$submission->sid}/edit", $url_options);
+          break;
+        case 'email-values':
+          $email = $data['webform-email'];
+          // Token for the entire form tree for e-mails.
+          if (!empty($email)) {
+            $node = node_load($submission->nid);
+            $format = isset($email['html']) && $email['html'] ? 'html' : 'text';
+            $replacements[$original] = webform_submission_render($node, $submission, $email, $format);
+          }
+          break;
+        default: // Webform submission tokens (caching per webform node)
+          $split = explode(':', $name);
+          $skey = $split[1];
+          $node = node_load($submission->nid);
+          foreach ($node->webform['components'] as $ckey => $cvalue) {
+            if ($cvalue['form_key'] == $skey) {
+              if ($split[0] == 'submission') {
+                $replacements[$original] = $submission->data[$ckey]['value'][0];
+              }
+              elseif ($split[0] == 'email') {
+                $replacements[$original] = $cvalue['name'] . ': ' . $submission->data[$ckey]['value'][0];
+              }
+            }
+            else {
+              continue;
+            }
+          }
+          break;
+      }
+    }
+
+    // Chained token relationships.
+    if (($node_tokens = token_find_with_prefix($tokens, 'node')) && $node = node_load($submission->nid)) {
+      $replacements = token_generate('node', $node_tokens, array('node' => $node), $options);
+    }
+    if ($date_tokens = token_find_with_prefix($tokens, 'date')) {
+      $replacements = token_generate('date', $date_tokens, array('date' => $submission->submitted), $options);
+    }
+    if (($user_tokens = token_find_with_prefix($tokens, 'user')) && $account = user_load($submission->uid)) {
+      $replacements = token_generate('user', $user_tokens, array('user' => $account), $options);
+    }
+    if ($url_tokens = token_find_with_prefix($tokens, 'url')) {
+      $replacements = token_generate('url', $url_tokens, array('path' => url("node/{$submission->nid}/submission/{$submission->sid}", $url_options)), $options);
+    }
+  }
+
+  return $replacements;
+}
