Index: privatemsg.api.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/privatemsg/privatemsg.api.php,v
retrieving revision 1.1.2.5.2.6
diff -u -p -r1.1.2.5.2.6 privatemsg.api.php
--- privatemsg.api.php	16 Aug 2010 08:15:01 -0000	1.1.2.5.2.6
+++ privatemsg.api.php	20 Oct 2010 00:11:20 -0000
@@ -268,18 +268,6 @@ function hook_privatemsg_sql_unread_coun
  */
 
 /**
- * Is called after the message has been loaded.
- *
- * Return data will be merged with the $message array.
- *
- * @param $message
- *    Message array
- */
-function hook_privatemsg_message_load($message) {
-  return array('my_key' => 'my_value');
-}
-
-/**
  * Is called when a message is flushed.
  *
  * The message will be deleted from the database, remove any related data here.
Index: privatemsg.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/privatemsg/privatemsg.module,v
retrieving revision 1.70.2.30.2.91.2.64.2.89
diff -u -p -r1.70.2.30.2.91.2.64.2.89 privatemsg.module
--- privatemsg.module	7 Oct 2010 00:25:14 -0000	1.70.2.30.2.91.2.64.2.89
+++ privatemsg.module	20 Oct 2010 00:11:28 -0000
@@ -503,7 +503,11 @@ function privatemsg_thread_load($thread_
         $thread['from'] = $start + 1;
         $query->range($start, $max_amount);
       }
-      $thread['messages'] = privatemsg_message_load_multiple($query->execute()->fetchCol(), $thread['read_all'] ? NULL : $account);
+      $conditions = array();
+      if (!$thread['read_all']) {
+        $conditions['account'] = $account;
+      }
+      $thread['messages'] = privatemsg_message_load_multiple($query->execute()->fetchCol());
 
       // If there are no messages, don't allow access to the thread.
       if (empty($thread['messages'])) {
@@ -1021,29 +1025,6 @@ function privatemsg_sql_list($account, $
 }
 
 /**
- * Query function for loading a single or multiple messages.
- *
- * @param $pmids
- *   Array of pmids.
- * @param $account
- *   Account for which the messages should be loaded.
- */
-function privatemsg_sql_load($pmids, $account = NULL) {
-  $query = db_select('pm_message', 'pm')
-    ->fields('pm', array('mid', 'author', 'subject', 'body', 'timestamp', 'format', 'has_tokens'))
-    ->fields('pmi', array('is_new', 'thread_id'))
-    ->condition('pmi.mid', $pmids)
-    ->orderBy('pm.timestamp', 'ASC')
-    ->orderBy('pm.mid', 'ASC');
-  if($account) {
-    $query
-      ->condition('pmi.recipient', $account->uid)
-      ->condition('pmi.type', array('hidden', 'user'));
-  }
-  $query->join('pm_index', 'pmi', 'pm.mid = pmi.mid');
-  return $query;
-}
-/**
  * Query definition to load messages of one or multiple threads.
  *
  * @param $threads
@@ -1890,7 +1871,11 @@ function privatemsg_get_link($recipients
  * @ingroup api
  */
 function privatemsg_message_load($pmid, $account = NULL) {
-  $messages = privatemsg_message_load_multiple(array($pmid), $account);
+  $conditions = array();
+  if ($account) {
+    $conditions['account'] = $account;
+  }
+  $messages = privatemsg_message_load_multiple(array($pmid), $conditions);
   return current($messages);
 }
 
@@ -1905,26 +1890,8 @@ function privatemsg_message_load($pmid, 
  *
  * @ingroup api
  */
-function privatemsg_message_load_multiple($pmids, $account = NULL) {
-  // Avoid SQL error that would happen with an empty pm.mid IN () clause.
-  if (empty($pmids)) {
-    return array();
-  }
-  $result = _privatemsg_assemble_query('load', $pmids, $account)->execute();
-
-  $messages = array();
-  foreach ($result as $message) {
-    $message->user = $account;
-    // Load author of message.
-    if (!($message->author = _privatemsg_user_load($message->author))) {
-      // If user does not exist, load anonymous user.
-      $message->author = _privatemsg_user_load(0);
-    }
-    $messages[$message->mid] = $message;
-  }
-  field_attach_load('privatemsg_message', $messages);
-  module_invoke_all('privatemsg_message_load', $messages);
-  return $messages;
+function privatemsg_message_load_multiple(array $pmids, array $conditions = array(), $reset = FALSE) {
+  return entity_load('privatemsg_message', $pmids, $conditions, $reset);
 }
 
 /**
@@ -2210,6 +2177,8 @@ function privatemsg_entity_info() {
       'label' => t('Privatemsg'),
       'base table' => 'pm_message',
       'fieldable' => TRUE,
+      'controller class' => 'PrivatemsgMessageController',
+      'uri callback' => 'privatemsg_message_uri_callback',
       'entity keys' => array(
         'id' => 'mid',
       ),
@@ -2227,6 +2196,27 @@ function privatemsg_entity_info() {
 }
 
 /**
+ * Returns the URI for a private message.
+ *
+ * @param $message
+ *   Private message object.
+ *
+ * @return
+ *   URI array as defined by hook_entity_info().
+ */
+function privatemsg_message_uri_callback($message) {
+  $uri = array(
+    'path' => 'messages/view/' . $message->thread_id,
+    'options' => array(),
+  );
+  // Add message fragment, if necessary.
+  if ($message->mid != $message->thread_id) {
+    $uri['options']['fragment'] = 'privatemsg-message-' . $message->mid;
+  }
+  return $uri;
+}
+
+/**
  * Implements hook_build_modes().
  */
 function privatemsg_build_modes($obj_type) {
@@ -2659,15 +2649,27 @@ function privatemsg_token_info() {
     'needs-data' => 'privatemsg_message',
   );
 
-  // Core tokens for nodes.
+  // Core tokens for private messages.
   $message['mid'] = array(
     'name' => t("Message ID"),
     'description' => t("The unique ID of the message."),
   );
+  $message['thread_id'] = array(
+    'name' => t("Thread ID"),
+    'description' => t("The unique ID of the thread."),
+  );
+  $message['url'] = array(
+    'name' => t("URL"),
+    'description' => t("URL that points to the message."),
+  );
   $message['subject'] = array(
     'name' => t("Subject"),
     'description' => t("The subject of the message."),
   );
+  $message['body'] = array(
+    'name' => t("Body"),
+    'description' => t("The body of the message."),
+  );
 
   // Chained tokens for nodes.
   $message['sent'] = array(
@@ -2696,8 +2698,8 @@ function privatemsg_token_info() {
  * Implements hook_tokens().
  */
 function privatemsg_tokens($type, $tokens, array $data = array(), array $options = array()) {
-
   global $user;
+  $url_options = array('absolute' => TRUE);
   if (isset($options['language'])) {
     $url_options['language'] = $options['language'];
     $language_code = $options['language']->language;
@@ -2729,6 +2731,21 @@ function privatemsg_tokens($type, $token
           $replacements[$original] = $sanitize ? check_plain($subject) : $subject;
           break;
 
+        case 'body':
+          // Avoid recursion.
+          if (empty($options['privatemsg_recursion'])) {
+            $body = privatemsg_token_replace($message->body, $data, $options + array('privatemsg_recursion' => 1));
+          }
+          else {
+            $body = $message->body;
+          }
+          $replacements[$original] = $sanitize ? check_plain($body) : $body;
+          break;
+
+        case 'url':
+          $uri = entity_uri('privatemsg_message', $message);
+          $replacements[$original] = url($uri['path'], $url_options + $uri['options']);
+
         // Default values for the chained tokens handled below.
         case 'author':
           $replacements[$original] = $sanitize ? filter_xss($message->author->name) : $message->author->name;
@@ -2829,4 +2846,46 @@ function privatemsg_token_replace($text,
 
   // If the user is a recipient, use default token_replace() function.
   return token_replace($text, $data, $options);
+}
+
+/**
+ * Private message controller, loads private messages.
+ */
+class PrivatemsgMessageController extends DrupalDefaultEntityController {
+
+  protected $account = NULL;
+
+  protected function attachLoad(&$messages, $revision_id = FALSE) {
+    global $user;
+    foreach ($messages as $message) {
+      $message->user = $this->account ? $this->account : $user;
+      // Load author of message.
+      if (!($message->author = _privatemsg_user_load($message->author))) {
+        // If user does not exist, load anonymous user.
+        $message->author = _privatemsg_user_load(0);
+      }
+    }
+    parent::attachLoad($messages, $revision_id);
+  }
+
+  protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
+    // Remove account from conditions.
+    if (isset($conditions['account'])) {
+      $this->account = $conditions['account'];
+      unset($conditions['account']);
+    }
+
+    $query = parent::buildQuery($ids, $conditions, $revision_id);
+    $query
+      ->fields('pmi', array('is_new', 'thread_id'))
+      ->orderBy('base.timestamp', 'ASC')
+      ->orderBy('base.mid', 'ASC');
+    if($this->account) {
+      $query
+        ->condition('pmi.recipient', $this->account->uid)
+        ->condition('pmi.type', array('hidden', 'user'));
+    }
+    $query->join('pm_index', 'pmi', 'base.mid = pmi.mid');
+    return $query;
+  }
 }
\ No newline at end of file
Index: privatemsg.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/privatemsg/privatemsg.test,v
retrieving revision 1.2.2.1.2.39
diff -u -p -r1.2.2.1.2.39 privatemsg.test
--- privatemsg.test	7 Oct 2010 00:25:14 -0000	1.2.2.1.2.39
+++ privatemsg.test	20 Oct 2010 00:11:34 -0000
@@ -37,7 +37,7 @@ class PrivatemsgTestCase extends DrupalW
    * Create user with no 'read privatemsg' permission. Try to access mailbox and see if it gives access denied error
    * Create user with 'read privatemsg' permission. Try to access mailbox and see if it gives allows access
    */
-  function dtestPrivatemsgReadPrivatemsgPermission() {
+  function testPrivatemsgReadPrivatemsgPermission() {
     $user_no_read_msg = $this->drupalCreateUser(); // set up user with default permissions (meaning: no read privatemsg permission
     $author     = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
     $recipient  = $this->drupalCreateUser(array('read privatemsg'));
@@ -73,7 +73,7 @@ class PrivatemsgTestCase extends DrupalW
    * Create user with no 'write privatemsg' permission. Try to access Write New Message page and see if it gives access denied error
    * Create user with 'write privatemsg' permission. Try to access Write New Message page and see if it gives allows access
    */
-  function dtestPrivatemsgWritePrivatemsgPermission() {
+  function testPrivatemsgWritePrivatemsgPermission() {
     $user_no_write_msg = $this->drupalCreateUser(); // set up user with default permissions (meaning: no read privatemsg permission
     $this->drupalLogin($user_no_write_msg);
     $this->drupalGet('messages/new');
@@ -85,7 +85,7 @@ class PrivatemsgTestCase extends DrupalW
     $this->assertResponse(200, t('HTTP Response 200: Access to Write New Message page was authorized to user with "<em>write privatemsg</em>" permission'));
   }
 
-  function dtestPaging() {
+  function testPaging() {
     $author     = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
     $recipient  = $this->drupalCreateUser(array('read privatemsg'));
 
@@ -369,7 +369,7 @@ class PrivatemsgTestCase extends DrupalW
   /**
    * Test functionality around disabling private messaging.
    */
-  function dtestDisablePrivatemsg() {
+  function testDisablePrivatemsg() {
     $admin_user = $this->drupalCreateUser(array('administer permissions'));
     $enableduser = $this->drupalCreateUser(array('read privatemsg', 'write privatemsg')); // set up user with read/write privatemsg permissions
     $enableduser2 = $this->drupalCreateUser(array('read privatemsg', 'write privatemsg')); // set up user with read/write privatemsg permissions
@@ -449,7 +449,7 @@ class PrivatemsgTestCase extends DrupalW
   /**
    * Test correct handling of read all permissions.
    */
-  function dtestReadAllPermission() {
+  function testReadAllPermission() {
     $author    = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
     $recipient = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
     $admin     = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg', 'read all private messages'));
@@ -513,7 +513,7 @@ class PrivatemsgTestCase extends DrupalW
   /**
    * Tests for the flush feature
    */
-  function dtestPrivatemsgFlush()
+  function testPrivatemsgFlush()
   {
     $author = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
     $recipient = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
@@ -569,7 +569,7 @@ class PrivatemsgTestCase extends DrupalW
     $this->assertTrue(empty($message), t('Message #4 has been flushed'));
   }
 
-  function dtestDelete() {
+  function testDelete() {
     // Create users.
     $author = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg', 'delete privatemsg'));
     $recipient = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg', 'delete privatemsg'));
@@ -728,7 +728,7 @@ class PrivatemsgTestCase extends DrupalW
   /**
    * Test preview functionality.
    */
-  function dtestPreview() {
+  function testPreview() {
     $user = $this->drupalCreateUser(array('read privatemsg', 'write privatemsg'));
 
     // Enable preview button.
@@ -748,7 +748,7 @@ class PrivatemsgTestCase extends DrupalW
   /**
    * Test autocomplete.
    */
-  function dtestAutocomplete() {
+  function testAutocomplete() {
     $current = $this->drupalCreateUser(array('read privatemsg', 'write privatemsg'));
     $user1 = $this->drupalCreateUser(array('read privatemsg', 'write privatemsg'));
     $user2 = $this->drupalCreateUser(array('read privatemsg', 'write privatemsg'));
@@ -815,7 +815,7 @@ class PrivatemsgFieldsTestCase extends D
     parent::setUp('privatemsg');
   }
 
-  function dtestSingleField() {
+  function testSingleField() {
     $admin = $this->drupalCreateUser(array('administer privatemsg settings', 'write privatemsg', 'read privatemsg'));
     $user = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
 
@@ -874,7 +874,7 @@ class PrivatemsgFieldsTestCase extends D
   /**
    * Verify that a message with an empty image can be sent.
    */
-  function dtestEmptyImage() {
+  function testEmptyImage() {
     $admin = $this->drupalCreateUser(array('administer privatemsg settings', 'write privatemsg', 'read privatemsg'));
     $user = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
 
@@ -940,7 +940,7 @@ class PrivatemsgLinksTestCase extends Dr
   /**
    * Tests author links displayed on nodes and comments.
    */
-  function dtestAuthorLinks() {
+  function testAuthorLinks() {
     $admin = $this->drupalCreateUser(array('read privatemsg', 'write privatemsg', 'administer privatemsg settings', 'create article content', 'create page content'));
     $user = $this->drupalCreateUser(array('read privatemsg', 'write privatemsg'));
 
@@ -1001,7 +1001,7 @@ class PrivatemsgLinksTestCase extends Dr
   /**
    * Tests menu block.
    */
-  function dtestMenuBlock() {
+  function testMenuBlock() {
     $admin = $this->drupalCreateUser(array('read privatemsg', 'write privatemsg', 'administer privatemsg settings', 'administer blocks', 'administer menu'));
     $user = $this->drupalCreateUser(array('read privatemsg'));
 
@@ -1040,7 +1040,7 @@ class PrivatemsgLinksTestCase extends Dr
   /**
    * Tests menu block.
    */
-  function dtestNewBlock() {
+  function testNewBlock() {
     $admin = $this->drupalCreateUser(array('read privatemsg', 'write privatemsg', 'administer privatemsg settings', 'administer blocks', 'administer menu'));
     $user = $this->drupalCreateUser(array('read privatemsg'));
 
@@ -1094,7 +1094,7 @@ class PrivatemsgTokenTestCase extends Dr
   /**
    * Basic token functionality.
    */
-  function dtestBasicTokens() {
+  function testBasicTokens() {
     $author = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg', 'use tokens in privatemsg'));
     $recipient1 = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
     $recipient2 = $this->drupalCreateUser(array('write privatemsg', 'read privatemsg'));
Index: pm_block_user/pm_block_user.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/privatemsg/pm_block_user/pm_block_user.module,v
retrieving revision 1.1.2.8.2.20
diff -u -p -r1.1.2.8.2.20 pm_block_user.module
--- pm_block_user/pm_block_user.module	7 Oct 2010 00:25:14 -0000	1.1.2.8.2.20
+++ pm_block_user/pm_block_user.module	20 Oct 2010 00:11:36 -0000
@@ -291,9 +291,9 @@ function pm_block_user_privatemsg_block_
   return $blocked;
 }
 
-function pm_block_user_query_privatemsg_load_alter($query) {
+function pm_block_user_query_privatemsg_message_load_multiple_alter($query) {
   $query->addField('pmbu', 'recipient', 'is_blocked');
-  $query->leftJoin('pm_block_user', 'pmbu', "pm.author = pmbu.author AND pmi.recipient = pmbu.recipient AND pmi.type = 'user'");
+  $query->leftJoin('pm_block_user', 'pmbu', "base.author = pmbu.author AND pmi.recipient = pmbu.recipient AND pmi.type = 'user'");
 }
 
 /**
