Index: privatemsg.api.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/privatemsg/privatemsg.api.php,v
retrieving revision 1.1.2.5.2.7
diff -u -p -r1.1.2.5.2.7 privatemsg.api.php
--- privatemsg.api.php	13 Oct 2010 15:30:11 -0000	1.1.2.5.2.7
+++ privatemsg.api.php	21 Oct 2010 09:14:23 -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.91
diff -u -p -r1.70.2.30.2.91.2.64.2.91 privatemsg.module
--- privatemsg.module	16 Oct 2010 07:15:49 -0000	1.70.2.30.2.91.2.64.2.91
+++ privatemsg.module	21 Oct 2010 09:14:31 -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'])) {
@@ -1028,29 +1032,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
@@ -1897,7 +1878,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);
 }
 
@@ -1912,26 +1897,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);
 }
 
 /**
@@ -2217,6 +2184,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',
       ),
@@ -2234,6 +2203,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) {
@@ -2666,15 +2656,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(
@@ -2703,8 +2705,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;
@@ -2713,6 +2715,11 @@ function privatemsg_tokens($type, $token
     $language_code = NULL;
   }
 
+  $recipient = $user;
+  if (isset($data['privatemsg_recipient'])) {
+    $recipient = $data['privatemsg_recipient'];
+  }
+
   $sanitize = !empty($options['sanitize']);
   $replacements = array();
   if ($type == 'privatemsg_message' && !empty($data['privatemsg_message'])) {
@@ -2736,13 +2743,29 @@ 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']);
+          break;
+
         // Default values for the chained tokens handled below.
         case 'author':
           $replacements[$original] = $sanitize ? filter_xss($message->author->name) : $message->author->name;
           break;
 
         case 'recipient':
-            $replacements[$original] = $sanitize ? filter_xss($user->name) : $user->name;
+            $replacements[$original] = $sanitize ? filter_xss($recipient->name) : $recipient->name;
           break;
 
         case 'sent':
@@ -2756,7 +2779,7 @@ function privatemsg_tokens($type, $token
     }
 
     if ($recipient_tokens = token_find_with_prefix($tokens, 'recipient')) {
-      $replacements += token_generate('user', $recipient_tokens, array('user' => $user), $options);
+      $replacements += token_generate('user', $recipient_tokens, array('user' => $recipient), $options);
     }
 
     if ($sent_tokens = token_find_with_prefix($tokens, 'sent')) {
@@ -2882,4 +2905,46 @@ function privatemsg_entity_property_info
     ),
   );
   return $info;
+}
+
+/**
+ * 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: 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.21
diff -u -p -r1.1.2.8.2.21 pm_block_user.module
--- pm_block_user/pm_block_user.module	13 Oct 2010 15:30:11 -0000	1.1.2.8.2.21
+++ pm_block_user/pm_block_user.module	21 Oct 2010 09:14:36 -0000
@@ -312,9 +312,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'");
 }
 
 /**
