Index: modules/simpletest/drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v
retrieving revision 1.222
diff -u -p -r1.222 drupal_web_test_case.php
--- modules/simpletest/drupal_web_test_case.php	28 Jun 2010 19:57:34 -0000	1.222
+++ modules/simpletest/drupal_web_test_case.php	29 Jun 2010 14:23:36 -0000
@@ -1101,7 +1101,8 @@ class DrupalWebTestCase extends DrupalTe
     // Make a request to the logout page, and redirect to the user page, the
     // idea being if you were properly logged out you should be seeing a login
     // screen.
-    $this->drupalGet('user/logout', array('query' => array('destination' => 'user')));
+    $this->drupalGet('user/logout');
+    $this->drupalGet('user');
     $pass = $this->assertField('name', t('Username field found.'), t('Logout'));
     $pass = $pass && $this->assertField('pass', t('Password field found.'), t('Logout'));
 
@@ -2964,7 +2965,9 @@ class DrupalWebTestCase extends DrupalTe
   }
 
   /**
-   * Assert that the most recently sent e-mail message has a field with the given value.
+   * Asserts that the most recently sent e-mail message has the given value.
+   *
+   * The field in $name must have the content described in $value.
    *
    * @param $name
    *   Name of field or message property to assert. Examples: subject, body, id, ...
@@ -2972,6 +2975,7 @@ class DrupalWebTestCase extends DrupalTe
    *   Value of the field to assert.
    * @param $message
    *   Message to display.
+   *
    * @return
    *   TRUE on pass, FALSE on fail.
    */
@@ -2982,13 +2986,76 @@ class DrupalWebTestCase extends DrupalTe
   }
 
   /**
-   * Log verbose message in a text file.
+   * Asserts that the most recently sent e-mail message has the string in it.
+   *
+   * @param $field_name
+   *   Name of field or message property to assert: subject, body, id, ...
+   * @param $string
+   *   String to search for.
+   * @param $email_depth
+   *   Number of emails to search for string, starting with most recent.
+   *
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  protected function assertMailString($field_name, $string, $email_depth) {
+    $mails = $this->drupalGetMails();
+    $string_found = FALSE;
+    for ($i = sizeof($mails) -1; $i >= sizeof($mails) - $email_depth && $i >= 0; $i--) {
+      $mail = $mails[$i];
+      // Normalize whitespace, as we don't know what the mail system might have
+      // done. Any run of whitespace becomes a single space.
+      $normalized_mail = preg_replace('/\s+/', ' ', $mail[$field_name]);
+      $normalized_string = preg_replace('/\s+/', ' ', $string);
+      $string_found = (FALSE !== strpos($normalized_mail, $normalized_string));
+      if ($string_found) {
+        break;
+      }
+    }
+    return $this->assertTrue($string_found, t('Expected text found in @field of email message: "@expected".', array('@field' => $field_name, '@expected' => $string)));
+  }
+
+  /**
+   * Asserts that the most recently sent e-mail message has the pattern in it.
+   *
+   * @param $field_name
+   *   Name of field or message property to assert: subject, body, id, ...
+   * @param $regex
+   *   Pattern to search for.
+   *
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  protected function assertMailPattern($field_name, $regex, $message) {
+    $mails = $this->drupalGetMails();
+    $mail = end($mails);
+    $regex_found = preg_match("/$regex/", $mail[$field_name]);
+    return $this->assertTrue($regex_found, t('Expected text found in @field of email message: "@expected".', array('@field' => $field_name, '@expected' => $regex)));
+  }
+
+  /**
+   * Outputs to verbose the most recent $count emails sent.
+   *
+   * @param $count
+   *   Optional number of emails to output.
+   */
+  protected function verboseEmail($count = 1) {
+    $mails = $this->drupalGetMails();
+    for ($i = sizeof($mails) -1; $i >= sizeof($mails) - $count && $i >= 0; $i--) {
+      $mail = $mails[$i];
+      $this->verbose(t('Email:') . '<pre>' . print_r($mail, TRUE) . '</pre>');
+    }
+  }
+
+  /**
+   * Logs verbose message in a text file.
    *
    * The a link to the vebose message will be placed in the test results via
    * as a passing assertion with the text '[verbose message]'.
    *
    * @param $message
    *   The verbose message to be stored.
+   *
    * @see simpletest_verbose()
    */
   protected function verbose($message) {
@@ -3001,7 +3068,7 @@ class DrupalWebTestCase extends DrupalTe
 }
 
 /**
- * Log verbose message in a text file.
+ * Logs verbose message in a text file.
  *
  * If verbose mode is enabled then page requests will be dumped to a file and
  * presented on the test result screen. The messages will be placed in a file
@@ -3013,8 +3080,10 @@ class DrupalWebTestCase extends DrupalTe
  *   The original file directory, before it was changed for testing purposes.
  * @param $test_class
  *   The active test case class.
+ *
  * @return
  *   The ID of the message to be placed in related assertion messages.
+ *
  * @see DrupalTestCase->originalFileDirectory
  * @see DrupalWebTestCase->verbose()
  */
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.942
diff -u -p -r1.942 system.module
--- modules/system/system.module	26 Jun 2010 21:32:20 -0000	1.942
+++ modules/system/system.module	29 Jun 2010 14:23:37 -0000
@@ -2861,7 +2861,7 @@ function system_action_info() {
       'type' => 'user',
       'label' => t('Ban IP address of current user'),
       'configurable' => FALSE,
-      'triggers' => array(),
+      'triggers' => array('any'),
     ),
     'system_goto_action' => array(
       'type' => 'system',
Index: modules/trigger/trigger.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/trigger/trigger.module,v
retrieving revision 1.62
diff -u -p -r1.62 trigger.module
--- modules/trigger/trigger.module	11 Apr 2010 18:33:44 -0000	1.62
+++ modules/trigger/trigger.module	29 Jun 2010 14:23:37 -0000
@@ -145,9 +145,6 @@ function trigger_trigger_info() {
        ),
      ),
      'user' => array(
-       'user_presave' => array(
-         'label' => t('When either creating a new user account or updating an existing'),
-       ),
        'user_insert' => array(
          'label' => t('After creating a new user account'),
        ),
@@ -488,18 +485,11 @@ function trigger_user_login(&$edit, $acc
  * Implements hook_user_logout().
  */
 function trigger_user_logout($account) {
-  $edit = NULL;
+  $edit = array();
   _trigger_user('user_logout', $edit, $account);
 }
 
 /**
- * Implements hook_user_presave().
- */
-function trigger_user_presave(&$edit, $account, $category) {
-  _trigger_user('user_presave', $edit, $account, $category);
-}
-
-/**
  * Implements hook_user_insert().
  */
 function trigger_user_insert(&$edit, $account, $category) {
@@ -528,7 +518,8 @@ function trigger_user_cancel($edit, $acc
  * Implements hook_user_delete().
  */
 function trigger_user_delete($account) {
-  _trigger_user('user_delete', $edit, $account, $method);
+  $edit = array();
+  _trigger_user('user_delete', $edit, $account, NULL);
 }
 
 /**
@@ -557,7 +548,7 @@ function _trigger_user($hook, &$edit, $a
       if (!isset($objects[$type])) {
         $objects[$type] = _trigger_normalize_user_context($type, $account);
       }
-      $context['account'] = $account;
+      $context['user'] = $account;
       actions_do($aid, $objects[$type], $context);
     }
     else {
Index: modules/trigger/trigger.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/trigger/trigger.test,v
retrieving revision 1.32
diff -u -p -r1.32 trigger.test
--- modules/trigger/trigger.test	1 May 2010 08:12:23 -0000	1.32
+++ modules/trigger/trigger.test	29 Jun 2010 14:23:37 -0000
@@ -2,7 +2,7 @@
 // $Id: trigger.test,v 1.32 2010/05/01 08:12:23 dries Exp $
 
 /**
- * Class with common helper methods.
+ * Provides common helper methods.
  */
 class TriggerWebTestCase extends DrupalWebTestCase {
 
@@ -14,6 +14,7 @@ class TriggerWebTestCase extends DrupalW
    * @param $edit
    *   The $edit array for the form to be used to configure.
    *   Example members would be 'actions_label' (always), 'message', etc.
+   *
    * @return
    *   the aid (action id) of the configured action, or FALSE if none.
    */
@@ -30,7 +31,7 @@ class TriggerWebTestCase extends DrupalW
 }
 
 /**
- * Test node triggers.
+ * Provides tests for node triggers.
  */
 class TriggerContentTestCase extends TriggerWebTestCase {
   var $_cleanup_roles = array();
@@ -49,7 +50,9 @@ class TriggerContentTestCase extends Tri
   }
 
   /**
-   * Various tests, all in one function to assure they happen in the right order.
+   * Tests several content-oriented trigger issues.
+   *
+   * These are in one function to assure they happen in the right order.
    */
   function testActionsContent() {
     global $user;
@@ -98,8 +101,10 @@ class TriggerContentTestCase extends Tri
   }
 
   /**
-   * Test that node actions are fired for each node individually if acting on
-   * multiple nodes.
+   * Tests multiple node actions.
+   *
+   * Verifies that node actions are fired for each node individually, if acting
+   * on multiple nodes.
    */
   function testActionContentMultiple() {
     // Assign an action to the node save/update trigger.
@@ -127,10 +132,13 @@ class TriggerContentTestCase extends Tri
   }
 
   /**
-   * Helper function for testActionsContent(): returns some info about each of the content actions.
+   * Returns some info about each of the content actions.
+   *
+   * This is helper function for testActionsContent().
    *
    * @param $action
    *   The name of the action to return info about.
+   *
    * @return
    *   An associative array of info about the action.
    */
@@ -172,7 +180,7 @@ class TriggerContentTestCase extends Tri
 }
 
 /**
- * Test cron trigger.
+ * Tests cron trigger.
  */
 class TriggerCronTestCase extends TriggerWebTestCase {
   public static function getInfo() {
@@ -188,7 +196,7 @@ class TriggerCronTestCase extends Trigge
   }
 
   /**
-   * Test assigning multiple actions to the cron trigger.
+   * Tests assigning multiple actions to the cron trigger.
    *
    * This test ensures that both simple and multiple complex actions
    * succeed properly. This is done in the cron trigger test because
@@ -239,7 +247,296 @@ class TriggerCronTestCase extends Trigge
 }
 
 /**
- * Test other triggers.
+ * Provides a base class with trigger assignments and test comparisons.
+ */
+class TriggerActionTestCase extends TriggerWebTestCase {
+
+  function setUp() {
+    parent::setUp('trigger');
+  }
+
+  /**
+   * Creates a message with tokens.
+   *
+   * @param $trigger
+   *
+   * @return
+   *   A message with embedded tokens.
+   */
+  function generateMessageWithTokens($trigger) {
+    // Note that subject is limited to 254 characters in action configuration.
+    $message = t('Action was triggered by trigger @trigger user:name=[user:name] user:uid=[user:uid] user:mail=[user:mail] user:url=[user:url] user:edit-url=[user:edit-url] user:created=[user:created]',
+      array('@trigger' => $trigger));
+    return trim($message);
+  }
+
+  /**
+   * Generates a comparison message to match the pre-token-replaced message.
+   *
+   * @param $trigger
+   *   Trigger, like 'user_login'.
+   * @param $account
+   *   Associated user account.
+   *
+   * @return
+   *   The token-replaced equivalent message. This does not use token
+   *   functionality.
+   *
+   * @see generateMessageWithTokens()
+   */
+  function generateTokenExpandedComparison($trigger, $account) {
+    // Note that user:last-login was omitted because it changes and can't
+    // be properly verified.
+    $message = t('Action was triggered by trigger @trigger user:name=@username user:uid=@uid user:mail=@mail user:url=@user_url user:edit-url=@user_edit_url user:created=@user_created',
+       array(
+        '@trigger' => $trigger,
+        '@username' => $account->name,
+        '@uid' => !empty($account->uid) ? $account->uid : t('not yet assigned'),
+        '@mail' => $account->mail,
+        '@user_url' => !empty($account->uid) ? url("user/$account->uid", array('absolute' => TRUE)) : t('not yet assigned'),
+        '@user_edit_url' => !empty($account->uid) ? url("user/$account->uid/edit", array('absolute' => TRUE)) : t('not yet assigned'),
+        '@user_created' => isset($account->created) ? format_date($account->created, 'medium') : t('not yet created'),
+        )
+      );
+      return trim($message);
+  }
+
+
+  /**
+   * Assigns a simple (non-configurable) action to a trigger.
+   *
+   * @param $trigger
+   *   The trigger to assign to, like 'user_login'.
+   * @param $action
+   *   The simple action to be assigned, like 'comment_insert'.
+   */
+  function assignSimpleAction($trigger, $action) {
+    $form_name = "trigger_{$trigger}_assign_form";
+    $form_html_id = strtr($form_name, '_', '-');
+    $edit = array('aid' => drupal_hash_base64($action));
+    $trigger_type = preg_replace('/_.*/', '', $trigger);
+    $this->drupalPost("admin/structure/trigger/$trigger_type", $edit, t('Assign'), array(), array(), $form_html_id);
+    $actions = trigger_get_assigned_actions($trigger);
+    $this->assertTrue(!empty($actions[$action]), t('Simple action @action assigned to trigger @trigger', array('@action' => $action, '@trigger' => $trigger)));
+  }
+
+  /**
+   * Assigns a system message action to the passed-in trigger.
+   *
+   * @param $trigger
+   *   For example, 'user_login'
+   */
+  function assignSystemMessageAction($trigger) {
+    $form_name = "trigger_{$trigger}_assign_form";
+    $form_html_id = strtr($form_name, '_', '-');
+    // Assign a configurable action 'System message' to the passed trigger.
+    $action_edit = array(
+      'actions_label' => $trigger . "_system_message_action_" . $this->randomName(16),
+      'message' => $this->generateMessageWithTokens($trigger),
+    );
+
+    // Configure an advanced action that we can assign.
+    $aid = $this->configureAdvancedAction('system_message_action', $action_edit);
+
+    $edit = array('aid' => drupal_hash_base64($aid));
+    $this->drupalPost('admin/structure/trigger/user', $edit, t('Assign'), array(), array(), $form_html_id);
+  }
+
+
+  /**
+   * Assigns a system_send_email_action to the passed-in trigger.
+   *
+   * @param $trigger
+   *   For example, 'user_login'
+   */
+  function assignSystemEmailAction($trigger) {
+    $form_name = "trigger_{$trigger}_assign_form";
+    $form_html_id = strtr($form_name, '_', '-');
+
+    $message = $this->generateMessageWithTokens($trigger);
+    // Assign a configurable action 'System message' to the passed trigger.
+    $action_edit = array(
+      // 'actions_label' => $trigger . "_system_send_message_action_" . $this->randomName(16),
+      'actions_label' => $trigger . "_system_send_email_action",
+      'recipient' => '[user:mail]',
+      'subject' => $message,
+      'message' => $message,
+    );
+
+    // Configure an advanced action that we can assign.
+    $aid = $this->configureAdvancedAction('system_send_email_action', $action_edit);
+
+    $edit = array('aid' => drupal_hash_base64($aid));
+    $this->drupalPost('admin/structure/trigger/user', $edit, t('Assign'), array(), array(), $form_html_id);
+  }
+
+  /**
+   * Asserts correct token replacement in both system message and email.
+   *
+   * @param $trigger
+   *   A trigger like 'user_login'.
+   * @param $account
+   *   The user account which triggered the action.
+   * @param $email_depth
+   *   Number of emails to scan, starting with most recent.
+   */
+  function assertSystemMessageAndEmailTokenReplacement($trigger, $account, $email_depth = 1) {
+    $this->assertSystemMessageTokenReplacement($trigger, $account);
+    $this->assertSystemEmailTokenReplacement($trigger, $account, $email_depth);
+  }
+
+  /**
+   * Asserts correct token replacement for the given trigger and account.
+   *
+   * @param $trigger
+   *   A trigger like 'user_login'.
+   * @param $account
+   *   The user account which triggered the action.
+   */
+  function assertSystemMessageTokenReplacement($trigger, $account) {
+    $expected = $this->generateTokenExpandedComparison($trigger, $account);
+    $this->assertText($expected,
+      t('Expected system message to contain token-replaced text "@expected" found in configured system message action', array('@expected' => $expected )) );
+  }
+
+
+  /**
+   * Asserts correct token replacement for the given trigger and account.
+   *
+   * @param $trigger
+   *   A trigger like 'user_login'.
+   * @param $account
+   *   The user account which triggered the action.
+   * @param $email_depth
+   *   Number of emails to scan, starting with most recent.
+   */
+  function assertSystemEmailTokenReplacement($trigger, $account, $email_depth = 1) {
+    $this->verboseEmail($email_depth);
+    $expected = $this->generateTokenExpandedComparison($trigger, $account);
+    $this->assertMailString('subject', $expected, $email_depth);
+    $this->assertMailString('body', $expected, $email_depth);
+    $this->assertMail('to', $account->mail, t('Mail sent to correct destination'));
+  }
+}
+
+/**
+ * Tests token substitution in trigger actions.
+ *
+ * This tests nearly every permutation of user triggers with system actions
+ * and checks the token replacement.
+ */
+class TriggerUserTokenTestCase extends TriggerActionTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Test user triggers',
+      'description' => 'Test user triggers and system actions with token replacement.',
+      'group' => 'Trigger',
+    );
+  }
+
+
+  /**
+   * Tests a variety of token replacements in actions.
+   */
+  function testUserTriggerTokenReplacement() {
+    $test_user = $this->drupalCreateUser(array('administer actions', 'administer users', 'change own username', 'access user profiles'));
+    $this->drupalLogin($test_user);
+
+    $triggers = array('user_login', 'user_insert', 'user_update', 'user_delete', 'user_logout', 'user_view');
+    foreach ($triggers as $trigger) {
+      $this->assignSystemMessageAction($trigger);
+      $this->assignSystemEmailAction($trigger);
+    }
+
+    $this->drupalLogout();
+    $this->assertSystemEmailTokenReplacement('user_logout', $test_user);
+
+    $this->drupalLogin($test_user);
+    $this->assertSystemMessageAndEmailTokenReplacement('user_login', $test_user, 2);
+    $this->assertSystemMessageAndEmailTokenReplacement('user_view', $test_user, 2);
+
+    $this->drupalPost("user/{$test_user->uid}/edit", array('name' => $test_user->name . '_changed'), t('Save'));
+    $test_user->name .= '_changed'; // Since we just changed it.
+    $this->assertSystemMessageAndEmailTokenReplacement('user_update', $test_user, 2);
+
+    $this->drupalGet('user');
+    $this->assertSystemMessageAndEmailTokenReplacement('user_view', $test_user);
+
+    $new_user = $this->drupalCreateUser(array('administer actions', 'administer users', 'cancel account', 'access administration pages'));
+    $this->assertSystemEmailTokenReplacement('user_insert', $new_user);
+
+    $this->drupalLogin($new_user);
+    $user_to_delete = $this->drupalCreateUser(array('access content'));
+    variable_set('user_cancel_method', 'user_cancel_delete');
+
+    $this->drupalPost("user/{$user_to_delete->uid}/cancel", array(), t('Cancel account'));
+    $this->assertSystemMessageAndEmailTokenReplacement('user_delete', $user_to_delete);
+  }
+
+
+}
+
+/**
+ * Tests token substitution in trigger actions.
+ *
+ * This tests nearly every permutation of user triggers with system actions
+ * and checks the token replacement.
+ */
+class TriggerUserActionTestCase extends TriggerActionTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Test user actions',
+      'description' => 'Test user actions.',
+      'group' => 'Trigger',
+    );
+  }
+
+  /**
+   * Tests user action assignment and execution.
+   */
+  function testUserActionAssignmentExecution() {
+    $test_user = $this->drupalCreateUser(array('administer actions', 'create article content', 'access comments', 'administer comments', 'post comments without approval', 'edit own comments'));
+    $this->drupalLogin($test_user);
+
+    $triggers = array('comment_presave', 'comment_insert', 'comment_update');
+    // system_block_ip_action is difficult to test without ruining the test.
+    $actions = array('user_block_user_action');
+    foreach ($triggers as $trigger) {
+      foreach ($actions as $action) {
+        $this->assignSimpleAction($trigger, $action);
+      }
+    }
+
+    $node = $this->drupalCreateNode(array('type' => 'article'));
+    $this->drupalPost("node/{$node->nid}", array('comment_body[und][0][value]' => t("my comment"), 'subject' => t("my comment subject")), t('Save'));
+    // Posting a comment should have blocked this user.
+    $account = user_load($test_user->uid, TRUE);
+    $this->assertTrue($account->status == 0, t('Account is blocked'));
+    $comment_author_uid = $account->uid;
+    // Now rehabilitate the comment author so it can be be blocked again when
+    // the comment is updated.
+    user_save($account, array('status' => TRUE));
+
+    $test_user = $this->drupalCreateUser(array('administer actions', 'create article content', 'access comments', 'administer comments', 'post comments without approval', 'edit own comments'));
+    $this->drupalLogin($test_user);
+
+    // Our original comment will have been comment 1.
+    $this->drupalPost("comment/1/edit", array('comment_body[und][0][value]' => t("my comment, updated"), 'subject' => t("my comment subject")), t('Save'));
+    $comment_author_account = user_load($comment_author_uid, TRUE);
+    $this->assertTrue($comment_author_account->status == 0, t('Comment author account (uid=@uid) is blocked after update to comment', array('@uid' => $comment_author_uid)));
+
+    // Verify that the comment was updated.
+    $test_user = $this->drupalCreateUser(array('administer actions', 'create article content', 'access comments', 'administer comments', 'post comments without approval', 'edit own comments'));
+    $this->drupalLogin($test_user);
+
+    $this->drupalGet("node/$node->nid");
+    $this->assertText(t("my comment, updated"));
+    $this->verboseEmail();
+  }
+}
+
+/**
+ * Tests other triggers.
  */
 class TriggerOtherTestCase extends TriggerWebTestCase {
   var $_cleanup_roles = array();
@@ -258,7 +555,7 @@ class TriggerOtherTestCase extends Trigg
   }
 
   /**
-   * Test triggering on user create and user login.
+   * Tests triggering on user create and user login.
    */
   function testActionsUser() {
     // Assign an action to the create user trigger.
@@ -315,7 +612,7 @@ class TriggerOtherTestCase extends Trigg
   }
 
   /**
-   * Test triggering on comment save.
+   * Tests triggering on comment save.
    */
   function testActionsComment() {
     // Assign an action to the comment save trigger.
@@ -344,7 +641,7 @@ class TriggerOtherTestCase extends Trigg
   }
 
   /**
-   * Test triggering on taxonomy new term.
+   * Tests triggering on taxonomy new term.
    */
   function testActionsTaxonomy() {
     // Assign an action to the taxonomy term save trigger.
@@ -382,7 +679,7 @@ class TriggerOtherTestCase extends Trigg
 }
 
 /**
- * Test that orphaned actions are properly handled.
+ * Tests that orphaned actions are properly handled.
  */
 class TriggerOrphanedActionsTestCase extends DrupalWebTestCase {
 
@@ -399,7 +696,7 @@ class TriggerOrphanedActionsTestCase ext
   }
 
   /**
-   * Test logic around orphaned actions.
+   * Tests logic around orphaned actions.
    */
   function testActionsOrphaned() {
     $action = 'trigger_test_generic_any_action';
Index: modules/trigger/tests/trigger_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/trigger/tests/trigger_test.module,v
retrieving revision 1.7
diff -u -p -r1.7 trigger_test.module
--- modules/trigger/tests/trigger_test.module	26 Mar 2010 20:44:10 -0000	1.7
+++ modules/trigger/tests/trigger_test.module	29 Jun 2010 14:23:37 -0000
@@ -35,7 +35,6 @@ function trigger_test_action_info() {
         'comment_insert',
         'comment_update',
         'comment_delete',
-        'user_presave',
         'user_insert',
         'user_update',
         'user_delete',
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1178
diff -u -p -r1.1178 user.module
--- modules/user/user.module	28 Jun 2010 03:25:21 -0000	1.1178
+++ modules/user/user.module	29 Jun 2010 14:23:37 -0000
@@ -3373,7 +3373,7 @@ function user_action_info() {
       'label' => t('Block current user'),
       'type' => 'user',
       'configurable' => FALSE,
-      'triggers' => array(),
+      'triggers' => array('any'),
     ),
   );
 }
@@ -3384,22 +3384,25 @@ function user_action_info() {
  * @ingroup actions
  */
 function user_block_user_action(&$entity, $context = array()) {
+  // First priority: If there is a $entity->uid, block that user.
+  // This is most likely a user object or the author if a node or comment.
   if (isset($entity->uid)) {
     $uid = $entity->uid;
   }
   elseif (isset($context['uid'])) {
     $uid = $context['uid'];
   }
+  // If neither of those are valid, then block the current user.
   else {
-    global $user;
-    $uid = $user->uid;
+    $uid = $GLOBALS['user']->uid;
   }
   db_update('users')
     ->fields(array('status' => 0))
     ->condition('uid', $uid)
     ->execute();
   drupal_session_destroy_uid($uid);
-  watchdog('action', 'Blocked user %name.', array('%name' => $user->name));
+  $account = user_load($uid);
+  watchdog('action', 'Blocked user %name.', array('%name' => $account->name));
 }
 
 /**
Index: modules/user/user.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.tokens.inc,v
retrieving revision 1.6
diff -u -p -r1.6 user.tokens.inc
--- modules/user/user.tokens.inc	20 Apr 2010 09:48:06 -0000	1.6
+++ modules/user/user.tokens.inc	29 Jun 2010 14:23:37 -0000
@@ -81,7 +81,8 @@ function user_tokens($type, $tokens, arr
       switch ($name) {
         // Basic user account information.
         case 'uid':
-          $replacements[$original] = $account->uid;
+          // In the case of hook user_presave uid is not set yet.
+          $replacements[$original] = !empty($account->uid) ? $account->uid : t('not yet assigned');
           break;
 
         case 'name':
@@ -94,20 +95,21 @@ function user_tokens($type, $tokens, arr
           break;
 
         case 'url':
-          $replacements[$original] = url("user/$account->uid", $url_options);
+          $replacements[$original] = !empty($account->uid) ? url("user/$account->uid", $url_options) : t('not yet assigned');
           break;
 
         case 'edit-url':
-          $replacements[$original] = url("user/$account->uid/edit", $url_options);
+          $replacements[$original] = !empty($account->uid) ? url("user/$account->uid/edit", $url_options) : t('not yet assigned');
           break;
 
         // These tokens are default variations on the chained tokens handled below.
         case 'last-login':
-          $replacements[$original] = format_date($account->login, 'medium', '', NULL, $language_code);
+          $replacements[$original] = !empty($account->login) ? format_date($account->login, 'medium', '', NULL, $language_code) : t('never');
           break;
 
         case 'created':
-          $replacements[$original] = format_date($account->created, 'medium', '', NULL, $language_code);
+          // In the case of user_presave the created date may not yet be set.
+          $replacements[$original] = !empty($account->created) ? format_date($account->created, 'medium', '', NULL, $language_code) : t('not yet created');
           break;
       }
     }
