? 0-field-tokens-D7.patch
? 0-temp.patch
? 0-token-get-info-type-D7.patch
? 0-token-lightestterm-D7.patch
? 0-token-upgrade-info-D7.patch
? 0-user-text-field-tokens.txt
? 125640-token-profile-D7.patch
? 776952-token-blocks-D7.patch
? 821008-token-devel-D7.patch
? 829744-token-entity-path-D7.patch
? 844912-token-search-tokens-D7.patch
? 845146-token-presort-tokens-D7.patch
? 845148-token-user-email-tree-D7.patch
? jquery-ui-tree
? jstree
? menu.tokens.inc
? patches
? temp-D7.patch
? temp-token-entity-id-D7.patch
? tests
? token.inc
? treeTable
Index: token.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/token/token.module,v
retrieving revision 1.46
diff -u -p -r1.46 token.module
--- token.module	9 Nov 2010 20:47:58 -0000	1.46
+++ token.module	13 Nov 2010 19:07:59 -0000
@@ -23,7 +23,7 @@ function token_help($path, $arg) {
  * Return an array of the core modules supported by token.module.
  */
 function _token_core_supported_modules() {
-  return array('book', 'menu');
+  return array('book', 'menu', 'profile');
 }
 
 /**
@@ -136,6 +136,34 @@ function token_library() {
 }
 
 /**
+ * Implements hook_form_alter().
+ *
+ * Adds a submit handler to forms which could affect the tokens available on
+ * the site.
+ */
+function token_form_alter(&$form, $form_state, $form_id) {
+  switch ($form_id) {
+    // Profile field forms.
+    case 'profile_field_form':
+    case 'profile_field_delete':
+      $form += array('#submit' => array());
+      array_unshift($form['#submit'], 'token_clear_cache');
+      break;
+  }
+}
+
+/**
+ * Clear token caches and static variables.
+ */
+function token_clear_cache() {
+  cache_clear_all(NULL, 'cache_token');
+  drupal_static_reset('token_get_info');
+  drupal_static_reset('token_get_global_token_types');
+  drupal_static_reset('token_build_tree');
+  drupal_static_reset('_token_profile_fields');
+}
+
+/**
  * Return an array of entity type to token type mappings.
  *
  * Why do we need this? Because when the token API was moved to core we did not
Index: token.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/token/token.test,v
retrieving revision 1.19
diff -u -p -r1.19 token.test
--- token.test	9 Nov 2010 21:24:46 -0000	1.19
+++ token.test	13 Nov 2010 19:07:59 -0000
@@ -28,6 +28,14 @@ class TokenTestHelper extends DrupalWebT
     }
   }
 
+  function assertNoTokens($type, $object, array $tokens, array $options = array()) {
+    $token_input = drupal_map_assoc($tokens);
+    $values = token_generate($type, $token_input, array($type => $object), $options);
+    foreach ($tokens as $token) {
+      $this->assertTrue(!isset($values[$token]), t("Token value for [@type:@token] was not generated.", array('@type' => $type, '@token' => $token)));
+    }
+  }
+
   function saveAlias($source, $alias, $language = LANGUAGE_NONE) {
     $alias = array(
       'source' => $source,
@@ -275,3 +283,134 @@ class TokenEntityTestCase extends TokenT
     return $term;
   }
 }
+
+/**
+ * Test the profile tokens.
+ */
+class TokenProfileTestCase extends TokenTestHelper {
+  private $account;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Profile token tests',
+      'description' => 'Test the profile tokens.',
+      'group' => 'Token',
+    );
+  }
+
+  function setUp() {
+    parent::setUp(array('profile'));
+    $this->account = $this->drupalCreateUser(array('administer users'));
+    $this->drupalLogin($this->account);
+  }
+
+  /**
+   * Test the profile tokens.
+   */
+  function testProfileTokens() {
+    $field_types = _profile_field_types();
+    foreach (array_keys($field_types) as $field_type) {
+      $field = array();
+      switch ($field_type) {
+        case 'checkbox':
+          $field['title'] = 'This is a checkbox';
+          break;
+        case 'selection':
+          $field['options'] = implode("\n", array('Red', 'Blue', 'Green'));
+          break;
+      }
+      $this->addProfileField($field_type, $field);
+    }
+
+    // Submit the profile fields for the user.
+    $edit = array(
+      'profile_textfield' => 'This is a text field',
+      'profile_textarea' => "First paragraph.\n\nSecond paragraph.",
+      'profile_checkbox' => TRUE,
+      'profile_selection' => 'Red',
+      'profile_list' => 'Drupal, Joomla',
+      'profile_url' => 'http://www.example.com/',
+      'profile_date[month]' => 5,
+      'profile_date[day]' => 20,
+      'profile_date[year]' => 1984,
+    );
+    $this->drupalPost("user/{$this->account->uid}/edit/SimpleTest", $edit, 'Save');
+    $account = user_load($this->account->uid, TRUE);
+
+    // Test the profile token values.
+    $tokens = array(
+      'profile-textfield' => 'This is a text field',
+      'profile-textarea' => "<p>First paragraph.</p>\n<p>Second paragraph.</p>\n",
+      'profile-checkbox' => 'This is a checkbox',
+      'profile-selection' => 'Red',
+      'profile-list' => 'Drupal, Joomla',
+      'profile-url' => 'http://www.example.com/',
+      'profile-date' => format_date(453859200, 'medium', '', NULL),
+      'profile-date:raw' => '453859200',
+      'profile-date:custom:Y' => '1984',
+    );
+    $this->assertTokens('user', $account, $tokens);
+
+    // 'Un-select' the checkbox and select profile fields.
+    $edit = array(
+      'profile_checkbox' => FALSE,
+      'profile_selection' => '0',
+    );
+    $this->drupalPost("user/{$this->account->uid}/edit/SimpleTest", $edit, 'Save');
+    $account = user_load($this->account->uid, TRUE);
+
+    // The checkbox and select profile tokens should no longer return a value.
+    $no_tokens = array(
+      'profile-checkbox',
+      'profile-selection',
+    );
+    $this->assertNoTokens('user', $account, $no_tokens);
+  }
+
+  /**
+   * Add a profile field.
+   *
+   * @param $type
+   *   The profile field type.
+   * @param $field
+   *   (optional) An array of the profile field properties.
+   *
+   * @return
+   *   The saved profile field record object.
+   *
+   * @see drupal_form_submit()
+   */
+  function addProfileField($type, array $field = array()) {
+    $field += array(
+      'type' => $type,
+      'category' => 'SimpleTest',
+      'title' => $this->randomName(8),
+      'name' => 'profile_' . $type,
+      'explanation' => $this->randomName(50),
+      'autocomplete' => 0,
+      'required' => 0,
+      'register' => 0,
+    );
+
+    // Submit the profile field using drupal_form_submit().
+    $form_state = array();
+    $form_state['values'] = $field;
+    $form_state['values']['op'] = 'Save field';
+    $form_state['build_info']['args'] = array($type);
+    drupal_form_submit('profile_field_form', $form_state);
+
+    // Verify the profile field was created successfully.
+    $saved_field = db_query("SELECT * FROM {profile_field} WHERE type = :type AND name = :name", array(':type' => $type, ':name' => $field['name']))->fetchObject();
+    $this->assertTrue($saved_field, t('Profile field @name created.', array('@name' => $saved_field->name)));
+
+    // Why in the hell these fields get saved with not the explict values
+    // for autocomplete, register and required that we tell it, I have no idea.
+    // So we need to manually save it again. Stupid profile module...
+    $saved_field->autocomplete = $field['autocomplete'];
+    $saved_field->required = $field['required'];
+    $saved_field->register = $field['register'];
+    drupal_write_record('profile_field', $saved_field, array('fid'));
+
+    return $saved_field;
+  }
+}
Index: token.tokens.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/token/token.tokens.inc,v
retrieving revision 1.26
diff -u -p -r1.26 token.tokens.inc
--- token.tokens.inc	9 Nov 2010 21:24:46 -0000	1.26
+++ token.tokens.inc	13 Nov 2010 19:07:59 -0000
@@ -466,3 +466,106 @@ function menu_tokens($type, $tokens, arr
 
   return $replacements;
 }
+
+/**
+ * Implements hook_token_info() on behalf of profile.module.
+ */
+function profile_token_info() {
+  $info = array();
+
+  foreach (_token_profile_fields() as $field) {
+    $info['tokens']['user'][$field->token_name] = array(
+      'name' => check_plain($field->title),
+      'description' => t('@category @type field.', array('@category' => drupal_ucfirst($field->category), '@type' => $field->type)),
+    );
+
+    switch ($field->type) {
+      case 'date':
+        $info['tokens']['user'][$field->token_name]['type'] = 'date';
+        break;
+    }
+  }
+
+  return $info;
+}
+
+/**
+ * Implements hook_tokens() on behalf of profile.module.
+ */
+function profile_tokens($type, $tokens, array $data = array(), array $options = array()) {
+  $replacements = array();
+  $sanitize = !empty($options['sanitize']);
+  $language_code = isset($options['language']) ? $options['language']->language : NULL;
+
+  if ($type == 'user' && !empty($data['user'])) {
+    $account = $data['user'];
+
+    // Load profile fields if this is the global user account.
+    // @see http://drupal.org/node/361471
+    // @see http://drupal.org/node/967330
+    if ($account->uid == $GLOBALS['user']->uid && isset($account->timestamp)) {
+      $profile_users = array($account->uid => $account);
+      profile_user_load($profile_users);
+      $account = $profile_users[$account->uid];
+    }
+
+    $profile_fields = _token_profile_fields();
+    foreach ($tokens as $name => $original) {
+      if (isset($profile_fields[$name]) && isset($account->{$profile_fields[$name]->name})) {
+        $value = $account->{$profile_fields[$name]->name};
+        switch ($profile_fields[$name]->type) {
+          case 'textarea':
+            $replacements[$original] = $sanitize ? check_markup($value, filter_default_format($account), '', TRUE) : $value;
+            break;
+          case 'date':
+            $timestamp = gmmktime(0, 0, 0, $value['month'], $value['day'], $value['year']);
+            $replacements[$original] = format_date($timestamp, 'medium', '', NULL, $language_code);
+            break;
+          case 'url':
+            $replacements[$original] = $sanitize ? check_url($value) : $value;
+            break;
+          case 'checkbox':
+            if ($value) {
+              // Checkbox field if checked should return the text.
+              $replacements[$original] = $sanitize ? check_plain($profile_fields[$name]->title) : $profile_fields[$name]->title;
+            }
+            break;
+          case 'selection':
+            if (!$value) {
+              continue;
+            }
+          default:
+            $replacements[$original] = $sanitize ? check_plain($value) : $value;
+            break;
+        }
+      }
+    }
+
+    // Chained token relationships.
+    foreach ($profile_fields as $field) {
+      if ($field->type == 'date' && isset($account->{$field->name}) && $field_tokens = token_find_with_prefix($tokens, $field->token_name)) {
+        $date = $account->{$field->name};
+        $replacements += token_generate('date', $field_tokens, array('date' => gmmktime(0, 0, 0, $date['month'], $date['day'], $date['year'])), $options);
+      }
+    }
+  }
+
+  return $replacements;
+}
+
+/**
+ * Fetch an array of profile field objects, keyed by token name.
+ */
+function _token_profile_fields() {
+  $fields = &drupal_static(__FUNCTION__);
+
+  if (!isset($fields)) {
+    $results = db_query("SELECT name, title, category, type FROM {profile_field}");
+    foreach ($results as $field) {
+      $field->token_name = strtr($field->name, '_', '-');
+      $fields[$field->token_name] = $field;
+    }
+  }
+
+  return $fields;
+}
