diff --git a/THEMING.txt b/THEMING.txt
index f0001b8..80bab49 100644
--- a/THEMING.txt
+++ b/THEMING.txt
@@ -111,5 +111,5 @@ The template file for theming submissions is webform-submission.tpl.php. You can
 use webform-submission-[node id here].tpl.php for individual nodes if desired.
 Note that the contents of this template are used not only for display of
 submissions in the Webform interface but also in e-mails when printing out
-the %email_values token.
+the [submission:values] token.
 
diff --git a/components/email.inc b/components/email.inc
index 9a65e7f..4efaef2 100644
--- a/components/email.inc
+++ b/components/email.inc
@@ -56,15 +56,15 @@ function _webform_edit_email($component) {
     '#size' => 60,
     '#maxlength' => 127,
     '#weight' => 0,
-    '#attributes' => ($component['value'] == '%useremail' && count(form_get_errors()) == 0) ? array('disabled' => TRUE) : array(),
+    '#attributes' => ($component['value'] == '[current-user:mail]' && count(form_get_errors()) == 0) ? array('disabled' => TRUE) : array(),
     '#id' => 'email-value',
   );
   $form['user_email'] = array(
     '#type' => 'checkbox',
     '#title' => t('User email as default'),
-    '#default_value' => $component['value'] == '%useremail' ? 1 : 0,
+    '#default_value' => $component['value'] == '[current-user:mail]' ? 1 : 0,
     '#description' => t('Set the default value of this field to the user email, if he/she is logged in.'),
-    '#attributes' => array('onclick' => 'getElementById("email-value").value = (this.checked ? "%useremail" : ""); getElementById("email-value").disabled = this.checked;'),
+    '#attributes' => array('onclick' => 'getElementById("email-value").value = (this.checked ? "[current-user:mail]" : ""); getElementById("email-value").disabled = this.checked;'),
     '#weight' => 0,
     '#element_validate' => array('_webform_edit_email_validate'),
   );
@@ -103,7 +103,7 @@ function _webform_edit_email($component) {
  */
 function _webform_edit_email_validate($element, &$form_state) {
   if ($form_state['values']['user_email']) {
-    $form_state['values']['value'] = '%useremail';
+    $form_state['values']['value'] = '[current-user:mail]';
   }
 }
 
diff --git a/components/hidden.inc b/components/hidden.inc
index e173c29..99e3210 100644
--- a/components/hidden.inc
+++ b/components/hidden.inc
@@ -73,8 +73,7 @@ function _webform_render_hidden($component, $value = NULL, $filter = TRUE) {
   // Set filtering options for "value" types, which are not displayed to the
   // end user so they do not need to be sanitized.
   $strict = $component['extra']['hidden_type'] != 'value';
-  $allow_anonymous = $component['extra']['hidden_type'] == 'value';
-  $default_value = $filter ? _webform_filter_values($component['value'], $node, NULL, NULL, $strict, $allow_anonymous) : $component['value'];
+  $default_value = $filter ? _webform_filter_values($component['value'], $node, NULL, NULL, $strict) : $component['value'];
   if (isset($value[0])) {
     $default_value = $value[0];
   }
diff --git a/includes/webform.admin.inc b/includes/webform.admin.inc
index f075987..a3be128 100644
--- a/includes/webform.admin.inc
+++ b/includes/webform.admin.inc
@@ -51,21 +51,21 @@ function webform_admin_settings() {
   $form['email']['webform_default_from_address']  = array(
     '#type' => 'textfield',
     '#title' => t('From address'),
-    '#default_value' => variable_get('webform_default_from_address', variable_get('site_mail', ini_get('sendmail_from'))),
+    '#default_value' => webform_variable_get('webform_default_from_address'),
     '#description' => t('The default sender address for emailed webform results; often the e-mail address of the maintainer of your forms.'),
   );
 
   $form['email']['webform_default_from_name']  = array(
     '#type' => 'textfield',
     '#title' => t('From name'),
-    '#default_value' => variable_get('webform_default_from_name', variable_get('site_name', '')),
+    '#default_value' => webform_variable_get('webform_default_from_name'),
     '#description' => t('The default sender name which is used along with the default from address.'),
   );
 
   $form['email']['webform_default_subject']  = array(
     '#type' => 'textfield',
     '#title' => t('Default subject'),
-    '#default_value' => variable_get('webform_default_subject', t('Form submission from: %title')),
+    '#default_value' => webform_variable_get('webform_default_subject'),
     '#description' => t('The default subject line of any e-mailed results.'),
   );
 
diff --git a/includes/webform.emails.inc b/includes/webform.emails.inc
index cfd7cb5..e61a9a8 100644
--- a/includes/webform.emails.inc
+++ b/includes/webform.emails.inc
@@ -284,7 +284,7 @@ function webform_email_edit_form($form, $form_state, $node, $email = array()) {
   );
 
   $form['template']['tokens'] = array(
-    '#markup' => theme('webform_token_help', array('groups' => 'all')),
+    '#markup' => theme('webform_token_help', array('groups' => array('node', 'submission'))),
   );
 
   $form['template']['components'] = array(
@@ -294,7 +294,7 @@ function webform_email_edit_form($form, $form_state, $node, $email = array()) {
     '#default_value' => array_diff(array_keys($node->webform['components']), $email['excluded_components']),
     '#multiple' => TRUE,
     '#size' => 10,
-    '#description' => t('The selected components will be included in the %email_values token. Individual values may still be printed if explicitly specified as a %email[key] in the template.'),
+    '#description' => t('The selected components will be included in the [submission:values] token. Individual values may still be printed if explicitly specified as a [submision:values:?] in the template.'),
     '#process' => array('webform_component_select'),
   );
 
@@ -435,7 +435,7 @@ function webform_email_edit_form_submit($form, &$form_state) {
 
   // Save the list of included components.
   // We actually maintain an *exclusion* list, so any new components will
-  // default to being included in the %email_values token until unchecked.
+  // default to being included in the [submission:values] token until unchecked.
   $included = array_keys(array_filter((array) $form_state['values']['components']));
   $excluded = array_diff(array_keys($node->webform['components']), $included);
   $email['excluded_components'] = $excluded;
diff --git a/includes/webform.pages.inc b/includes/webform.pages.inc
index 97c9395..1c0aabf 100644
--- a/includes/webform.pages.inc
+++ b/includes/webform.pages.inc
@@ -64,7 +64,7 @@ function webform_configure_form($form, &$form_state, $node) {
     '#type' => 'item',
     '#title' => t('Redirection location'),
     '#theme' => 'webform_advanced_redirection_form',
-    '#description' => t('Choose where to redirect the user upon successful submission.') . ' ' . t('The <em>Custom URL</em> option supports Webform token replacements.') . theme('webform_token_help', array('groups' => array('basic', 'node', 'special', 'submission'))),
+    '#description' => t('Choose where to redirect the user upon successful submission.') . ' ' . t('The <em>Custom URL</em> option supports Webform token replacements.') . theme('webform_token_help', array('groups' => array('node', 'webform-submission'))),
   );
   $form['submission']['redirection']['redirect']= array(
     '#type' => 'radios',
diff --git a/includes/webform.submissions.inc b/includes/webform.submissions.inc
index 925aff5..eddf3b0 100644
--- a/includes/webform.submissions.inc
+++ b/includes/webform.submissions.inc
@@ -196,7 +196,7 @@ function webform_submission_send_mail($node, $submission, $emails = NULL) {
     }
 
     // Replace tokens in the message.
-    $email['message'] = _webform_filter_values($email['message'], $node, $submission, $email, FALSE, TRUE);
+    $email['message'] = _webform_filter_values($email['message'], $node, $submission, $email, FALSE);
 
     // Build the e-mail headers.
     $email['headers'] = theme(array('webform_mail_headers_' . $node->nid, 'webform_mail_headers'), array('node' => $node, 'submission' => $submission, 'email' => $email));
@@ -386,7 +386,8 @@ function webform_submission_page($node, $submission, $format) {
     $output = drupal_get_form('webform_client_form_' . $node->nid, $node, $submission);
   }
   else {
-    $output = webform_submission_render($node, $submission, NULL, $format);
+    $renderable = webform_submission_render($node, $submission, NULL, $format);
+    $output = drupal_render($renderable);
   }
 
   // Determine the mode in which we're displaying this submission.
@@ -580,7 +581,7 @@ function webform_submission_render($node, $submission, $email, $format) {
   }
 
   drupal_alter('webform_submission_render', $renderable);
-  return drupal_render($renderable);
+  return $renderable;
 }
 
 /**
diff --git a/templates/webform-mail.tpl.php b/templates/webform-mail.tpl.php
index 401f2b2..cb8f5bd 100644
--- a/templates/webform-mail.tpl.php
+++ b/templates/webform-mail.tpl.php
@@ -19,18 +19,18 @@
  * when using the "default" e-mail template.
  */
 ?>
-<?php print ($email['html'] ? '<p>' : '') . t('Submitted on %date'). ($email['html'] ? '</p>' : ''); ?>
+<?php print ($email['html'] ? '<p>' : '') . t('Submitted on [submission:date:long]'). ($email['html'] ? '</p>' : ''); ?>
 
 <?php if ($user->uid): ?>
-<?php print ($email['html'] ? '<p>' : '') . t('Submitted by user: %username') . ($email['html'] ? '</p>' : ''); ?>
+<?php print ($email['html'] ? '<p>' : '') . t('Submitted by user: [submission:user]') . ($email['html'] ? '</p>' : ''); ?>
 <?php else: ?>
-<?php print ($email['html'] ? '<p>' : '') . t('Submitted by anonymous user: [%ip_address]') . ($email['html'] ? '</p>' : ''); ?>
+<?php print ($email['html'] ? '<p>' : '') . t('Submitted by anonymous user: [submission:ip-address]') . ($email['html'] ? '</p>' : ''); ?>
 <?php endif; ?>
 
 <?php print ($email['html'] ? '<p>' : '') . t('Submitted values are') . ':' . ($email['html'] ? '</p>' : ''); ?>
 
-%email_values
+[submission:values]
 
 <?php print ($email['html'] ? '<p>' : '') . t('The results of this submission may be viewed at:') . ($email['html'] ? '</p>' : '') ?>
 
-<?php print ($email['html'] ? '<p>' : ''); ?>%submission_url<?php print ($email['html'] ? '</p>' : ''); ?>
+<?php print ($email['html'] ? '<p>' : ''); ?>[submission:url]<?php print ($email['html'] ? '</p>' : ''); ?>
diff --git a/tests/submission.test b/tests/submission.test
index 84255a8..12e83f0 100644
--- a/tests/submission.test
+++ b/tests/submission.test
@@ -109,7 +109,8 @@ class WebformSubmissionTestCase extends WebformTestCase {
     $node = $this->testWebformForm();
     $submission_values = $value_type == 'sample' ? $this->testWebformPost() : array();
 
-    // Visit the node page with the "foo=bar" query, to test %get[] default values.
+    // Visit the node page with the "foo=bar" query, to test
+    // [current-page:query:?] default values.
     $this->drupalGet('node/' . $node->nid, array('query' => array('foo' => 'bar')));
     $this->assertText($node->body[LANGUAGE_NONE][0]['value'], t('Webform node created and accessible at !url', array('!url' => 'node/' . $node->nid)), t('Webform'));
 
diff --git a/tests/webform.test b/tests/webform.test
index fd20046..65a4b78 100644
--- a/tests/webform.test
+++ b/tests/webform.test
@@ -15,10 +15,26 @@ class WebformTestCase extends DrupalWebTestCase {
    */
   function setUp() {
     // Enable Webform.
-    parent::setUp('webform', 'profile');
+    parent::setUp('webform', 'token');
 
-    // Create a profile field to test %profile tokens.
-    db_query("INSERT INTO {profile_field} (title, name, explanation, category, type, weight, required, register, visibility, autocomplete, options, page) VALUES ('Gender', 'profile_gender', '', 'Profile', 'textfield', 0, 0, 0, 2, 0, '', '')");
+    // Create a profile field to test [user:?] tokens.
+    $field = array(
+      'field_name' => 'gender',
+      'type' => 'text',
+      'cardinality' => 1,
+    );
+    $instance = array(
+      'field_name' => 'gender',
+      'entity_type' => 'user',
+      'bundle' => 'user',
+      'label' => 'Gender',
+      'widget' => array(
+        'type' => 'text_textfield',
+        'label' => 'Gender',
+      ),
+    );
+    field_create_field($field);
+    field_create_instance($instance);
 
     // Create a normal user that can view their own submissions.
     $permissions['userAccess'] = array(
@@ -53,9 +69,8 @@ class WebformTestCase extends DrupalWebTestCase {
 
     foreach ($permissions as $user_key => $role_permissions) {
       $this->webform_users[$user_key] = $this->drupalCreateUser($role_permissions);
-      $profile = array('profile_gender' => 'Female');
-      $this->webform_users[$user_key]->profile_gender = 'Female';
-      profile_save_profile($profile, $this->webform_users[$user_key], 'Profile');
+      $this->webform_users[$user_key]->gender = array(LANGUAGE_NONE => array(array('value' => 'Female')));
+      user_save($this->webform_users[$user_key]);
     }
   }
 
@@ -396,7 +411,7 @@ class WebformTestCase extends DrupalWebTestCase {
           'form_key' => 'email',
           'name' => 'E-mail',
           'type' => 'email',
-          'value' => '%useremail',
+          'value' => '[current-user:mail]',
           'mandatory' => '0',
           'extra' => array(
             // SimpleTest does not support type="email" input fields.
@@ -449,7 +464,7 @@ class WebformTestCase extends DrupalWebTestCase {
           'form_key' => 'textfield_disabled',
           'name' => 'Textfield Disabled',
           'type' => 'textfield',
-          'value' => '%get[foo]',
+          'value' => '[current-page:query:foo]',
           'extra' => array(
             'disabled' => 1,
           ),
@@ -457,7 +472,9 @@ class WebformTestCase extends DrupalWebTestCase {
           'pid' => '0',
           'weight' => '-15',
         ),
-        'sample values' => NULL,
+        // Manually hard-code the input if token is not available.
+        // TODO: Update after http://drupal.org/node/1347790 is finished.
+        'sample values' => module_exists('token') ? NULL : 'bar',
         'database values' => array('bar'),
         'database default values' => array('bar'),
       ),
@@ -466,7 +483,7 @@ class WebformTestCase extends DrupalWebTestCase {
           'form_key' => 'textfield_profile',
           'name' => 'Textfield Profile',
           'type' => 'textfield',
-          'value' => '%profile[profile_gender]',
+          'value' => '[current-user:gender]',
           'extra' => array(
             'width' => '20',
           ),
@@ -476,7 +493,9 @@ class WebformTestCase extends DrupalWebTestCase {
         ),
         'sample values' => 'Female',
         'database values' => array('Female'),
-        'database default values' => array($this->webform_users['admin']->profile_gender),
+        // The default value will be blank if token does not exist.
+        // TODO: Update after http://drupal.org/node/1347790 is finished.
+        'database default values' => module_exists('token') ? array($this->webform_users['admin']->gender[LANGUAGE_NONE][0]['value']) : '',
       ),
 
       // Test time components.
diff --git a/webform.install b/webform.install
index 19dacf4..4a4aaaf 100644
--- a/webform.install
+++ b/webform.install
@@ -229,7 +229,7 @@ function webform_schema() {
         'not null' => FALSE,
       ),
       'excluded_components' => array(
-        'description' => 'A list of components that will not be included in the %email_values token. A list of CIDs separated by commas.',
+        'description' => 'A list of components that will not be included in the [submission:values] token. A list of CIDs separated by commas.',
         'type' => 'text',
         'not null' => TRUE,
       ),
@@ -831,3 +831,161 @@ 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.
+ *
+ * If needed, please download and install the Token module from drupal.org.
+ * Otherwise some tokens will not be rendered. 
+ */
+function webform_update_7401(&$sandbox) {
+  // Define replacements.
+  $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',
+    '/%profile\[([^\]]+)\]/m',
+  );
+  $replacements = array(
+    '[current-user:name]',
+    '[current-user:mail]',
+    '[current-user:uid]',
+    '[submission:date:long]',
+    '[current-user:ip-address]',
+    '[site:name]',
+    '[node:nid]',
+    '[node:title]',
+    '[submission:values]',
+    '[submission:url]',
+    '[submission:sid]',
+  );
+  $dreplacements = array(
+    '[current-page:query:$1]',
+    '[submission:email:$2]',
+    '[submission:email:$1]',
+    '[submission:submission:$2]',
+    '[submission:submission:$1]',
+    '[current-user:$1]',
+  );
+
+  // Set up the initial batch process.
+  if (!isset($sandbox['progress'])) {
+    $sandbox['progress'] = 0;
+    $sandbox['last_nid_processed'] = -1;
+    $sandbox['max'] = db_select('webform')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+
+    // Update tokens in variables.
+    $variables = array(
+      'webform_default_subject',
+      'webform_default_from_name',
+      'webform_default_from_address',
+    );
+    foreach ($variables as $variable) {
+      $value = variable_get($variable, NULL);
+      if ($value !== NULL) {
+        $value = str_replace($patterns, $replacements, $value);
+        $value = preg_replace($dpatterns, $dreplacements, $value);
+        variable_set($variable, $value);
+      }
+    }
+  }
+
+  $limit = variable_get('webform_update_batch_size', 100);
+  $webforms = db_select('webform', 'w')
+    ->fields('w')
+    ->condition('nid', $sandbox['last_nid_processed'], '>')
+    ->orderBy('nid', 'ASC')
+    ->range(0, $limit)
+    ->execute()
+    ->fetchAllAssoc('nid', PDO::FETCH_ASSOC);
+
+  foreach ($webforms as $nid => $webform) {
+    // Update the webform record itself.
+    $original = $webform;
+    $parts = array(
+      'confirmation',
+      'redirect_url',
+    );
+    foreach ($parts as $part) {
+      $webform[$part] = str_replace($patterns, $replacements, $webform[$part]);
+      $webform[$part] = preg_replace($dpatterns, $dreplacements, $webform[$part]);
+    }
+    if ($webform != $original) {
+      drupal_write_record('webform', $webform, array('nid'));
+    }
+
+    // Update tokens in component configurations.
+    $result = db_select('webform_component', 'wc', array('fetch' => PDO::FETCH_ASSOC))
+      ->fields('wc')
+      ->condition('wc.nid', $nid)
+      ->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'));
+      }
+    }
+
+    // Update tokens in e-mail configurations.
+    $result = db_select('webform_emails', 'we', array('fetch' => PDO::FETCH_ASSOC))
+      ->fields('we')
+      ->condition('we.nid', $nid)
+      ->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'));
+      }
+    }
+  }
+
+  // If less than limit was processed, the update process is finished.
+  if (count($webforms) < $limit || $sandbox['progress'] == $sandbox['max']) {
+    $finished = TRUE;
+  }
+
+  // If there's no max value then there's nothing to update and we're finished.
+  if (empty($sandbox['max']) || isset($finished)) {
+    $message = t('Your existing webforms have been upgraded to use the global Drupal 7 token system.');
+    if (!module_exists('token')) {
+      $message .= ' <strong>' . t('Please download and install the <a href="http://drupal.org/project/token" target="_blank">Token module</a>. Otherwise some tokens will not be rendered.') . '</strong>';
+    }
+    return $message;
+  }
+  else {
+    // Indicate our current progress to the batch update system.
+    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
+  }
+}
diff --git a/webform.module b/webform.module
index 6ff3c85..375d159 100644
--- a/webform.module
+++ b/webform.module
@@ -558,7 +558,7 @@ function webform_theme() {
       'pattern' => 'webform_mail_headers_[0-9]+',
     ),
     'webform_token_help' => array(
-      'variables' => array('groups' => array()),
+      'variables' => array('groups' => array('node')),
     ),
     // webform.admin.inc.
     'webform_admin_settings' => array(
@@ -2516,7 +2516,7 @@ function webform_client_form_submit($form, &$form_state) {
 
   // Clean up the redirect URL and filter it for webform tokens.
   $redirect_url = trim($node->webform['redirect_url']);
-  $redirect_url = _webform_filter_values($redirect_url, $node, $submission, NULL, FALSE, TRUE);
+  $redirect_url = _webform_filter_values($redirect_url, $node, $submission, NULL, FALSE);
 
 
   // Remove the domain name from the redirect.
@@ -2554,11 +2554,11 @@ function webform_client_form_submit($form, &$form_state) {
 
   // Show a message if manually set.
   if (isset($message)) {
-    drupal_set_message($message);
+    drupal_set_message(_webform_filter_values($message, $node));
   }
   // If redirecting and we have a confirmation message, show it as a message.
   elseif (!$is_draft && !$external_url && (!empty($redirect_url) && $redirect_url != '<confirmation>') && !empty($confirmation)) {
-    drupal_set_message(check_markup($confirmation, $node->webform['confirmation_format'], '', TRUE));
+    drupal_set_message(check_markup(_webform_filter_values($confirmation, $node), $node->webform['confirmation_format'], '', TRUE));
   }
 
   $form_state['redirect'] = $redirect;
@@ -2895,176 +2895,24 @@ function _webform_fetch_draft_sid($nid, $uid) {
  *   Boolean value indicating if the results should be run through check_plain.
  *   This is used any time the values will be output as HTML, but not in
  *   default values or e-mails.
- * @param $allow_anonymous
- *   Boolean value indicating if all tokens should be replaced for anonymous
- *   users, even if they contain sensitive user information such as %session or
- *   %ip_address. This is disabled by default to prevent user data from being
- *   preserved in the anonymous page cache and should only be used in
- *   non-cached situations, such as e-mails.
- */
-function _webform_filter_values($string, $node = NULL, $submission = NULL, $email = NULL, $strict = TRUE, $allow_anonymous = FALSE) {
-  global $user;
-  static $replacements;
-
+ */
+function _webform_filter_values($string, $node = NULL, $submission = NULL, $email = NULL, $strict = TRUE) {
   // Don't do any filtering if the string is empty.
   if (strlen(trim($string)) == 0) {
     return $string;
   }
 
-  // Setup default token replacements.
-  if (!isset($replacements)) {
-    $replacements['unsafe'] = array();
-    $replacements['safe']['%site'] = variable_get('site_name', 'drupal');
-    $replacements['safe']['%date'] = format_date(REQUEST_TIME, 'large');
-  }
-
-  // Node replacements.
-  if (isset($node) && !array_key_exists('%nid', $replacements['safe'])) {
-    $replacements['safe']['%nid'] = $node->nid;
-    $replacements['safe']['%title'] = $node->title;
-  }
-
-  // Determine the display format.
-  $format = isset($email['html']) && $email['html'] ? 'html' : 'text';
-
-  // Submission replacements.
-  if (isset($submission) && !isset($replacements['email'][$format])) {
-    module_load_include('inc', 'webform', 'includes/webform.components');
-
-    // Set the submission ID.
-    $replacements['unsafe']['%sid'] = $submission->sid;
-
-    // E-mails may be sent in two formats, keep tokens separate for each one.
-    $replacements['email'][$format] = array();
-
-    // Populate token values for each component.
-    foreach ($submission->data as $cid => $value) {
-      $component = $node->webform['components'][$cid];
-
-      // Find by form key.
-      $parents = webform_component_parent_keys($node, $component);
-      $form_key = implode('][', $parents);
-      $display_element = webform_component_invoke($component['type'], 'display', $component, $value['value'], $format);
-
-      // Ensure the component is added as a property.
-      $display_element['#webform_component'] = $component;
-
-      if (empty($display_element['#parents'])) {
-        $display_element['#parents'] = array_merge(array('submitted'), $parents);
-      }
-      if (empty($display_element['#id'])) {
-        $display_element['#id'] = drupal_html_id('edit-' . implode('-', $display_element['#parents']));
-      }
-      $replacements['email'][$format]['%email[' . $form_key . ']'] = render($display_element);
-      $display_element['#theme_wrappers'] = array(); // Remove label and wrappers.
-      $replacements['email'][$format]['%value[' . $form_key . ']'] = render($display_element);
-    }
-
-    // Provide blanks for components in the webform but not in the submission.
-    $missing_components = array_diff_key($node->webform['components'], $submission->data);
-    foreach ($missing_components as $component) {
-      $parents = webform_component_parent_keys($node, $component);
-      $form_key = implode('][', $parents);
-      $replacements['email'][$format]['%email[' . $form_key . ']'] = '';
-      $replacements['email'][$format]['%value[' . $form_key . ']'] = '';
-    }
-
-    // Submission edit URL.
-    $replacements['unsafe']['%submission_url'] = url('node/' . $node->nid . '/submission/' . $submission->sid, array('absolute' => TRUE));
-  }
-
-  // Token for the entire form tree for e-mails.
-  if (isset($submission) && isset($email)) {
-    $replacements['email'][$format]['%email_values'] = webform_submission_render($node, $submission, $email, $format);
-  }
-
-  // Provide a list of candidates for token replacement.
-  $special_tokens = array(
-    'safe' => array(
-      '%get' => $_GET,
-      '%post' => $_POST,
-    ),
-    'unsafe' => array(
-      '%cookie' => $_COOKIE,
-      '%session' => isset($_SESSION) ? $_SESSION : array(),
-      '%request' => $_REQUEST,
-      '%server' => $_SERVER,
-      '%profile' => (array) $user,
-    ),
-  );
-
-  // Replacements of global variable tokens.
-  if (!isset($replacements['specials_set'])) {
-    $replacements['specials_set'] = TRUE;
-
-    // Load profile information if available.
-    if ($user->uid) {
-      $account = user_load($user->uid);
-      $special_tokens['unsafe']['%profile'] = (array) $account;
-    }
-
-    // User replacements.
-    if (!array_key_exists('%uid', $replacements['unsafe'])) {
-      $replacements['unsafe']['%uid'] = !empty($user->uid) ? $user->uid : '';
-      $replacements['unsafe']['%username'] = isset($user->name) ? $user->name : '';
-      $replacements['unsafe']['%useremail'] = isset($user->mail) ? $user->mail : '';
-      $replacements['unsafe']['%ip_address'] = ip_address();
-    }
-
-    // Populate the replacements array with special variables.
-    foreach ($special_tokens as $safe_state => $tokens) {
-      foreach ($tokens as $token => $variable) {
-        // Safety check in case $_POST or some other global has been removed
-        // by a naughty module, in which case $variable may be NULL.
-        if (!is_array($variable)) {
-          continue;
-        }
-
-        foreach ($variable as $key => $value) {
-          // This special case for profile module dates.
-          if ($token == '%profile' && is_array($value) && isset($value['year'])) {
-            $replacement = webform_strtodate(webform_date_format(), $value['month'] . '/' . $value['day'] . '/' . $value['year'], 'UTC');
-          }
-          else {
-            // Checking for complex types (arrays and objects) fails here with
-            // incomplete objects (see http://php.net/is_object), so we check
-            // for simple types instead.
-            $replacement = (is_string($value) || is_bool($value) || is_numeric($value)) ? $value : '';
-          }
-          $replacements[$safe_state][$token . '[' . $key . ']'] = $replacement;
-        }
-      }
-    }
-  }
-
-  // Make a copy of the replacements so we don't affect the static version.
-  $safe_replacements = $replacements['safe'];
-
-  // Restrict replacements for anonymous users. Not all tokens can be used
-  // because they may expose session or other private data to other users when
-  // anonymous page caching is enabled.
-  if ($user->uid || $allow_anonymous) {
-    $safe_replacements += $replacements['unsafe'];
-    if (isset($replacements['email'][$format])) {
-      $safe_replacements += $replacements['email'][$format];
-    }
+  $token_data = array();
+  if ($node) {
+    $token_data['node'] = $node;
   }
-  else {
-    foreach ($replacements['unsafe'] as $key => $value) {
-      $safe_replacements[$key] = '';
-    }
+  if ($submission) {
+    $token_data['webform-submission'] = $submission;
   }
-
-  $find = array_keys($safe_replacements);
-  $replace = array_values($safe_replacements);
-  $string = str_replace($find, $replace, $string);
-
-  // Clean up any unused tokens.
-  foreach ($special_tokens as $safe_state => $tokens) {
-    foreach (array_keys($tokens) as $token) {
-      $string = preg_replace('/\\' . $token . '\[\w+\]/', '', $string);
-    }
+  if ($email) {
+    $token_data['webform-email'] = $email;
   }
+  $string = token_replace($string, $token_data, array('clear' => true));
 
   return $strict ? _webform_filter_xss($string) : $string;
 }
@@ -3170,7 +3018,7 @@ function webform_variable_get($variable) {
       $result = variable_get('webform_default_from_address', variable_get('site_mail', ini_get('sendmail_from')));
       break;
     case 'webform_default_subject':
-      $result = variable_get('webform_default_subject', t('Form submission from: %title'));
+      $result = variable_get('webform_default_subject', t('Form submission from: [node:title]'));
       break;
     case 'webform_node_types':
       $result = variable_get('webform_node_types', array('webform'));
@@ -3184,81 +3032,29 @@ function webform_variable_get($variable) {
 
 function theme_webform_token_help($variables) {
   $groups = $variables['groups'];
-  $groups = empty($groups) ? array('basic', 'node', 'special') : $groups;
-
-  static $tokens = array();
-
-  if (empty($tokens)) {
-    $tokens['basic'] = array(
-      'title' => t('Basic tokens'),
-      'tokens' => array(
-        '%username' => t('The name of the user if logged in. Blank for anonymous users.'),
-        '%useremail' => t('The e-mail address of the user if logged in. Blank for anonymous users.'),
-        '%ip_address' => t('The IP address of the user.'),
-        '%site' => t('The name of the site (i.e. %site_name)', array('%site_name' => variable_get('site_name', ''))),
-        '%date' => t('The current date, formatted according to the site settings.'),
-      ),
-    );
-
-    $tokens['node'] = array(
-      'title' => t('Node tokens'),
-      'tokens' => array(
-        '%nid' => t('The node ID.'),
-        '%title' => t('The node title.'),
-      ),
-    );
-
-    $tokens['special'] = array(
-      'title' => t('Special tokens'),
-      'tokens' => array(
-        '%profile[' . t('key') . ']' => t('Any user profile field or value, such as %profile[name] or %profile[profile_first_name]'),
-        '%get[' . t('key') . ']' => t('Tokens may be populated from the URL by creating URLs of the form http://example.com/my-form?foo=bar. Using the token %get[foo] would print "bar".'),
-        '%post[' . t('key') . ']' => t('Tokens may also be populated from POST values that are submitted by forms.'),
-      ),
-      'description' => t('In addition to %get and %post, the following super tokens may be used, though only with logged-in users: %server, %cookie, and %request. For example %server[HTTP_USER_AGENT] or %session[id].'),
-    );
-
-    $tokens['email'] = array(
-      'title' => t('E-mail tokens'),
-      'tokens' => array(
-        '%email_values' => t('All included components in a hierarchical structure.'),
-        '%email[' . t('key') . '] ' => t('A formatted value and field label. Elements may be accessed such as <em>%email[fieldset_a][key_b]</em>. Do not include quotes.'),
-        '%submission_url' => t('The URL for viewing the completed submission.'),
-      ),
-    );
-
-    $tokens['submission'] = array(
-      'title' => t('Submission tokens'),
-      'tokens' => array(
-        '%sid' => t('The unique submission ID.'),
-        '%value[key]' => t('A value without additional formatting. Elements may be accessed such as <em>%value[fieldset_a][key_b]</em>. Do not include quotes.'),
-      ),
-    );
-  }
-
-  $output = '';
-  $output .= '<p>' . t('You may use special tokens in this field that will be replaced with dynamic values.') . '</p>';
-
-  foreach ($tokens as $group_name => $group) {
-    if (!is_array($groups) || in_array($group_name, $groups)) {
-      $items = array();
-      foreach ($group['tokens'] as $token => $token_description) {
-        $items[] = $token . ' - ' . $token_description;
-      }
-      $output .= theme('item_list', array('items' => $items, 'title' => $group['title']));
-      $output .= isset($group['description']) ? '<p>' . $group['description']  . '</p>' : '';
-    }
-  }
 
   $fieldset = array(
     '#title' => t('Token values'),
     '#type' => 'fieldset',
     '#collapsible' => TRUE,
     '#collapsed' => TRUE,
-    '#children' => '<div>' . $output . '</div>',
     '#attributes' => array('class' => array('collapsible', 'collapsed')),
   );
-  return theme('fieldset', array('element' => $fieldset));
+
+  $help = '<p>' . t('This field supports dynamic token values. Common values might be [current-user:mail] or [node:title].') . '</p>';
+  if (!module_exists('token')) {
+    $help .= '<p>' . t('A full listing of tokens may be listed here by installing the <a href="http://drupal.org/project/token">Token module</a>.') . '</p>';
+  }
+  $fieldset['help'] = array(
+    '#markup' => $help,
+  );
+
+  $fieldset['token_tree'] = array(
+    '#theme' => 'token_tree',
+    '#token_types' => $groups,
+  );
+
+  return render($fieldset);
 }
 
 function _webform_safe_name($name) {
@@ -3344,15 +3140,15 @@ function webform_format_email_address($address, $name, $node = NULL, $submission
   // Address may be an array if a component value was used on checkboxes.
   if (is_array($address)) {
     foreach ($address as $key => $individual_address) {
-      $address[$key] = _webform_filter_values($individual_address, $node, $submission, NULL, FALSE, TRUE);
+      $address[$key] = _webform_filter_values($individual_address, $node, $submission, NULL, FALSE);
     }
   }
   else {
-    $address = _webform_filter_values($address, $node, $submission, NULL, FALSE, TRUE);
+    $address = _webform_filter_values($address, $node, $submission, NULL, FALSE);
   }
 
   if ($format == 'long' && !empty($name)) {
-    $name = _webform_filter_values($name, $node, $submission, NULL, FALSE, TRUE);
+    $name = _webform_filter_values($name, $node, $submission, NULL, FALSE);
     if ($encode) {
       $name = mime_header_encode($name);
     }
