? .git
? privatemsg_roles.patch
Index: privatemsg.install
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.install,v
retrieving revision 1.15
diff -u -p -r1.15 privatemsg.install
--- privatemsg.install	4 Jan 2010 15:56:56 -0000	1.15
+++ privatemsg.install	15 Jan 2010 13:38:46 -0000
@@ -24,8 +24,8 @@ function privatemsg_schema() {
         'not null'      => TRUE,
         'unsigned'      => TRUE,
       ),
-      'uid'     => array(
-        'description'   => 'UID of either the author or the recipient',
+      'recipient'     => array(
+        'description'   => 'ID of the recipient object, typically user',
         'type'          => 'int',
         'not null'      => TRUE,
         'unsigned'      => TRUE,
@@ -44,13 +44,20 @@ function privatemsg_schema() {
         'not null' => TRUE,
         'default' => 0
       ),
+      'type' => array(
+        'description'   => 'Type of recipient object',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => 'user'
+      ),
 
     ),
     'indexes'         => array(
       'mid'               => array('mid'),
       'thread_id'         => array('thread_id'),
-      'uid'         => array('uid'),
-      'is_new'              => array('mid', 'uid', 'is_new', ),
+      'recipient'         => array('recipient', 'type'),
+      'is_new'            => array('mid', 'recipient', 'type', 'is_new'),
     ),
   );
 
@@ -528,3 +535,29 @@ function privatemsg_update_6007() {
 
   return $ret;
 }
+
+/**
+ * Change schema to allow other recipients than single users.
+ */
+function privatemsg_update_6008() {
+  $ret = array();
+  db_drop_index($ret, 'pm_index', 'uid');
+  db_drop_index($ret, 'pm_index', 'is_new');
+  db_change_field($ret, 'pm_index', 'uid', 'recipient', array(
+    'description'   => 'ID of the recipient object, typically user',
+    'type'          => 'int',
+    'not null'      => TRUE,
+    'unsigned'      => TRUE,
+  ));
+  db_add_field($ret, 'pm_index', 'type', array(
+        'description'   => 'Type of recipient object',
+        'type' => 'varchar',
+        'not null' => TRUE,
+        'length'  => '255',
+        'default' => 'user'
+      ), array('indexes' => array(
+      'recipient'         => array('recipient', 'type'),
+      'is_new'            => array('mid', 'recipient', 'type', 'is_new')
+  )));
+  return $ret;
+}
Index: privatemsg.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.module,v
retrieving revision 1.116
diff -u -p -r1.116 privatemsg.module
--- privatemsg.module	7 Jan 2010 10:17:05 -0000	1.116
+++ privatemsg.module	15 Jan 2010 13:38:47 -0000
@@ -28,6 +28,7 @@ function privatemsg_perm() {
     'read all private messages',
     'administer privatemsg settings',
     'write privatemsg',
+    'write privatemsg to roles',
     'delete privatemsg',
   );
 }
@@ -66,6 +67,18 @@ function _privatemsg_generate_user_array
   return $participants;
 }
 
+function _privatemsg_format_participant($account) {
+  if (!empty($account->name)) {
+    if (!isset($account->uid)) {
+      $account->uid = $account->recipient;
+    }
+    return theme('username', $account);
+  }
+  else {
+    return $account->{$account->type . '_name'};
+  }
+}
+
 /**
  * Format an array of user objects.
  *
@@ -89,7 +102,7 @@ function _privatemsg_format_participants
         $limited = TRUE;
         break;
       }
-      $to[] = theme('username', $account);
+      $to[] = _privatemsg_format_participant($account);
     }
 
     $limit_string = '';
@@ -279,8 +292,7 @@ function privatemsg_view_access($thread)
  * @return TRUE if user has disabled private messaging, FALSE otherwise
  */
 function privatemsg_is_disabled(&$account) {
-
-  if (!$account || !$account->uid) {
+  if (!$account || !isset($account->uid) || !$account->uid) {
     return FALSE;
   }
 
@@ -338,12 +350,25 @@ function privatemsg_thread_load($thread_
       $query = _privatemsg_assemble_query('participants', $thread_id);
       $participants = db_query($query['query']);
       $thread['participants'] = array();
+      $exists = FALSE;
       while ($participant = db_fetch_object($participants)) {
-        $thread['participants'][$participant->uid] = $participant;
+        if ($participant->type == 'user' && $participant->recipient == $account->uid) {
+          $exists = TRUE;
+        }
+        $thread['participants'][$participant->type . '_' . $participant->recipient] = $participant;
       }
       $thread['read_all'] = FALSE;
-      if (!array_key_exists($account->uid, $thread['participants']) && privatemsg_user_access('read all private messages', $account)) {
-        $thread['read_all'] = TRUE;
+      if (!$exists) {
+        // Maybe the user is part of some other recipient type, call the hook.
+        foreach (module_implements('privatemsg_is_recipient') as $module) {
+          if (!$exists = module_invoke($module, 'privatemsg_is_recipient', $account, $thread['participants'])) {
+            break;
+          }
+        }
+        // Check $exists again.
+        if (!$exists && privatemsg_user_access('read all private messages', $account)) {
+          $thread['read_all'] = TRUE;
+        }
       }
 
       // Load messages returned by the messages query with privatemsg_message_load_multiple().
@@ -546,7 +571,7 @@ function privatemsg_preprocess_privatems
   $vars['mid'] = isset($message['mid']) ? $message['mid'] : NULL;
   $vars['thread_id'] = isset($message['thread_id']) ? $message['thread_id'] : NULL;
   $vars['author_picture'] = theme('user_picture', $message['author']);
-  $vars['author_name_link'] = theme('username', $message['author']);
+  $vars['author_name_link'] = _privatemsg_format_participant($message['author']);
   /**
    * @todo perhaps make this timestamp configurable via admin UI?
    */
@@ -594,8 +619,13 @@ function privatemsg_message_change_statu
     global $user;
     $account = $user;
   }
-  $query = "UPDATE {pm_index} SET is_new = %d WHERE mid = %d AND uid = %d";
+  $query = "UPDATE {pm_index} SET is_new = %d WHERE mid = %d AND recipient = %d AND type = 'user'";
   db_query($query, $status, $pmid, $account->uid);
+  if (db_affected_rows() == 0) {
+    $message = privatemsg_message_load($pmid);
+    $index_sql = "INSERT INTO {pm_index} (mid, thread_id, recipient, type, is_new, deleted) VALUES (%d, %d, %d, '%s', %d, %d)";
+    db_query($index_sql, $pmid, $message['thread_id'], $account->uid, 'user', 0, 0);
+  }
 }
 
 /**
@@ -629,9 +659,14 @@ function _privatemsg_load_thread_partici
   $query = _privatemsg_assemble_query('participants', $thread_id);
   $result = db_query($query['query']);
   $participants = array();
-  while ($uid = db_fetch_object($result)) {
-    if (($recipient = user_load($uid->uid))) {
-      $participants[$recipient->uid] = $recipient;
+  while ($participant = db_fetch_object($result)) {
+    if ($participant->type == 'user' && ($account = user_load($participant->recipient))) {
+      $participants['user_' . $account->uid] = $account;
+    }
+    elseif ($participant->type != 'user') {
+      // They key is always recipient.
+      $participant->key = 'recipient';
+      $participants[$participant->type . '_' . $participant->recipient] = $participant;
     }
   }
   return $participants;
@@ -666,8 +701,14 @@ function _privatemsg_parse_userstring($i
       foreach (module_implements('privatemsg_name_lookup') as $module) {
         $function = $module . '_privatemsg_name_lookup';
         if (($recipient = $function($string)) && is_object($recipient)) {
+          // Default to user object.
+          if (!isset($recipient->type)) {
+            $recipient->type = 'user';
+            $recipient->key = 'uid';
+          }
+          $recipients[$recipient->{$recipient->key}] = $recipient;
+
           // If there is a match, continue with the next input string.
-          $recipients[$recipient->uid] = $recipient;
           continue 2;
         }
       }
@@ -686,6 +727,28 @@ function _privatemsg_parse_userstring($i
   return array($recipients, $invalid);
 }
 
+function _privatemsg_build_where_clause($account) {
+  // Default parts.
+  $where       = "pmi.recipient = %d AND pmi.type = 'user'";
+  $args        = $account->uid;
+
+  // Fetch parts from other modules and merge defaults in.
+  $query_parts = module_invoke_all('privatemsg_recipients_where', $account);
+  if (!isset($query_parts['where'])) {
+    $query_parts['where'] = array();
+  }
+  if (!isset($query_parts['args'])) {
+    $query_parts['args'] = array();
+  }
+  array_unshift($query_parts['where'], $where);
+  array_unshift($query_parts['args'], $args);
+
+  // Merge the different where parts in a single string, splitted by OR.
+  $query_parts['where'] = '(' . implode(') OR (', $query_parts['where']) . ')';
+
+  return $query_parts;
+}
+
 /**
  * @addtogroup sql
  * @{
@@ -717,8 +780,10 @@ function privatemsg_sql_list(&$fragments
   $fragments['select'][]      = 'MIN(pm.subject) as subject';
   $fragments['select'][]      = 'MAX(pm.timestamp) as last_updated';
   // We use SUM so that we can count the number of unread messages.
-  $fragments['select'][]      = 'SUM(pmi.is_new) as is_new';
-
+  $read_messages = "(SELECT COUNT(DISTINCT pmi.mid) FROM {pm_index} pmie WHERE pmie.mid = pmi.mid AND pmie.recipient = %d AND pmie.is_new = 0 AND pmie.type = 'user' LIMIT 1)";
+  $fragments['select'][]      = "(COUNT(DISTINCT pmi.mid) - IF ($read_messages IS NULL, 0, $read_messages)) AS is_new";
+  $fragments['query_args']['select'][] = $account->uid;
+  $fragments['query_args']['select'][] = $account->uid;
   // Select number of messages in the thread if the count is
   // set to be displayed.
   if (in_array('count', $fields)) {
@@ -729,14 +794,14 @@ function privatemsg_sql_list(&$fragments
     // @todo: Replace this with a single query similiar to the tag list.
     if ($GLOBALS['db_type'] == 'pgsql') {
       // PostgreSQL does not know GROUP_CONCAT, so a subquery is required.
-      $fragments['select'][]      = "array_to_string(array(SELECT DISTINCT textin(int4out(pmia.uid))
+      $fragments['select'][]      = "array_to_string(array(SELECT DISTINCT textin(int4out(pmia.recipient))
                                                             FROM {pm_index} pmia
-                                                            WHERE pmia.thread_id = pmi.thread_id), ',') AS participants";
+                                                            WHERE pmia.type = 'user' AND pmia.thread_id = pmi.thread_id), ',') AS participants";
     }
     else {
-      $fragments['select'][]      = '(SELECT GROUP_CONCAT(DISTINCT pmia.uid SEPARATOR ",")
+      $fragments['select'][]      = "(SELECT GROUP_CONCAT(DISTINCT pmia.recipient SEPARATOR ',')
                                                             FROM {pm_index} pmia
-                                                            WHERE pmia.thread_id = pmi.thread_id) AS participants';
+                                                            WHERE pmia.type = 'user' AND pmia.thread_id = pmi.thread_id) AS participants";
     }
   }
   if (in_array('thread_started', $fields)) {
@@ -745,9 +810,12 @@ function privatemsg_sql_list(&$fragments
 
   $fragments['inner_join'][]  = 'INNER JOIN {pm_index} pmi ON pm.mid = pmi.mid';
 
-  // Only load undeleted messages of the current user and group by thread.
-  $fragments['where'][]       = 'pmi.uid = %d';
-  $fragments['query_args']['where'][]  = $account->uid;
+  // Build the query to load all messages for this user.
+  $query_parts = _privatemsg_build_where_clause($account);
+  $fragments['where'][]      = $query_parts['where'];
+  $fragments['query_args']['where'] = array_merge($fragments['query_args']['where'], $query_parts['args']);
+
+  // Only load undeleted messages and group by thread.
   $fragments['where'][]       = 'pmi.deleted = 0';
   $fragments['group_by'][]    = 'pmi.thread_id';
 
@@ -792,8 +860,10 @@ function privatemsg_sql_load(&$fragments
   $fragments['where'][]       = 'pmi.mid IN (' . db_placeholders($pmids) . ')';
   $fragments['query_args']['where']  += $pmids;
   if ($account) {
-    $fragments['where'][]       = 'pmi.uid = %d';
-    $fragments['query_args']['where'][]  = $account->uid;
+    // Build the query to load all messages for this user.
+    $query_parts = _privatemsg_build_where_clause($account);
+    $fragments['where'][]      = $query_parts['where'];
+    $fragments['query_args']['where'] = array_merge($fragments['query_args']['where'], $query_parts['args']);
   }
   $fragments['order_by'][] = 'pm.timestamp ASC';
 }
@@ -818,9 +888,10 @@ function privatemsg_sql_messages(&$fragm
   $fragments['query_args']['where']   += $threads;
   $fragments['inner_join'][]  = 'INNER JOIN {pm_message} pm ON (pm.mid = pmi.mid)';
   if ($account) {
-    // Only load the user's messages.
-    $fragments['where'][]     = 'pmi.uid = %d';
-    $fragments['query_args']['where'][]  = $account->uid;
+    // Build the query to load all messages for this user.
+    $query_parts = _privatemsg_build_where_clause($account);
+    $fragments['where'][]      = $query_parts['where'];
+    $fragments['query_args']['where'] = array_merge($fragments['query_args']['where'], $query_parts['args']);
   }
   if (!$load_all) {
     // Also load deleted messages when requested.
@@ -851,12 +922,18 @@ function privatemsg_sql_participants(&$f
 
   // Only load each participant once since they are listed as recipient for
   // every message of that thread.
-  $fragments['select'][]      = 'DISTINCT(pmi.uid) AS uid';
-  $fragments['select'][]      = 'u.name AS name';
+  $fragments['select'][]      = 'pmi.recipient';
+  $fragments['select'][]      = 'u.name';
+  $fragments['select'][]      = 'pmi.type';
 
-  $fragments['inner_join'][]  = 'INNER JOIN {users} u ON (u.uid = pmi.uid)';
+
+  $fragments['inner_join'][]  = "LEFT JOIN {users} u ON (u.uid = pmi.recipient AND pmi.type = 'user')";
   $fragments['where'][]       = 'pmi.thread_id = %d';
   $fragments['query_args']['where'][]  = $thread_id;
+
+  $fragments['group_by'][]    = 'pmi.recipient';
+  $fragments['group_by'][]    = 'u.name';
+  $fragments['group_by'][]    = 'pmi.type';
 }
 
 /**
@@ -870,13 +947,17 @@ function privatemsg_sql_participants(&$f
 function privatemsg_sql_unread_count(&$fragments, $account) {
   $fragments['primary_table'] = '{pm_index} pmi';
 
-  $fragments['select'][]      = 'COUNT(DISTINCT thread_id) as unread_count';
+  $query_parts = _privatemsg_build_where_clause($account);
+
+  $fragments['select'][]      = "(COUNT(DISTINCT thread_id) - (SELECT COUNT(DISTINCT thread_id) FROM {pm_index} pmie WHERE (" . $query_parts['where'] . ") AND pmi.deleted = 0 AND pmi.is_new = 1 AND pmi.type <> 'user')) as unread_count";
+  $fragments['query_args']['where'] = array_merge($fragments['query_args']['where'], $query_parts['args']);
 
   // Only count new messages that have not been deleted.
   $fragments['where'][]       = 'pmi.deleted = 0';
   $fragments['where'][]       = 'pmi.is_new = 1';
-  $fragments['where'][]       = 'pmi.uid = %d';
-  $fragments['query_args']['where'][]  = $account->uid;
+  // Build the query to load all messages for this user.
+  $fragments['where'][]      = $query_parts['where'];
+  $fragments['query_args']['where'] = array_merge($fragments['query_args']['where'], $query_parts['args']);
 }
 
 /**
@@ -908,6 +989,30 @@ function privatemsg_sql_autocomplete(&$f
   $fragments['where'][] = 'u.status <> 0';
   $fragments['order_by'][] = 'u.name ASC';
 }
+/**
+ * Query definition to search for username autocomplete suggestions.
+ *
+ * @param $fragments
+ *   Query fragments array.
+ * @param $search
+ *   Which search string is currently searched for.
+ * @param $names
+ *   Array of names not to be used as suggestions.
+ */
+function privatemsg_sql_autocomplete_roles(&$fragments, $search, $names) {
+  $fragments['primary_table'] = '{role} r';
+  $fragments['select'][] = 'r.name';
+
+  // Escape the % to get it through the placeholder replacement.
+  $fragments['where'][] = "r.name LIKE '%s'";
+  $fragments['query_args']['where'][] = $search .'%%';
+  if (!empty($names)) {
+    // If there are already names selected, exclude them from the suggestions.
+    $fragments['where'][] = "r.name NOT IN (". db_placeholders($names, 'text') .")";
+    $fragments['query_args']['where'] += $names;
+  }
+  $fragments['order_by'][] = 'r.name ASC';
+}
 
 /**
  * Query Builder function to load all messages that should be flushed.
@@ -1020,10 +1125,10 @@ function privatemsg_user($op, &$edit, &$
       }
 
       // Delete recipient entries of that user.
-      db_query('DELETE FROM {pm_index} WHERE uid = %d', $account->uid);
+      db_query("DELETE FROM {pm_index} WHERE recipient = %d and type = 'user'", $account->uid);
 
       // DELETE any disable flag for user.
-      db_query('DELETE from {pm_disable} where uid=%d', $account->uid);
+      db_query("DELETE from {pm_disable} WHERE uid = %d", $account->uid);
       break;
   }
 }
@@ -1123,7 +1228,12 @@ function privatemsg_message_change_delet
   }
 
   if ($account) {
-    db_query('UPDATE {pm_index} SET deleted = %d WHERE mid = %d AND uid = %d', $delete_value, $pmid, $account->uid);
+    db_query("UPDATE {pm_index} SET deleted = %d WHERE mid = %d AND recipient = %d AND type = 'user'", $delete_value, $pmid, $account->uid);
+    if (db_affected_rows() == 0) {
+      $message = privatemsg_message_load($pmid);
+      $index_sql = "INSERT INTO {pm_index} (mid, thread_id, recipient, type, is_new, deleted) VALUES (%d, %d, %d, '%s', %d, %d)";
+      db_query($index_sql, $pmid, $message['thread_id'], $account->uid, 'user', 0, 1);
+    }
   }
   else {
     // Mark deleted for all users.
@@ -1388,14 +1498,14 @@ function _privatemsg_send($message) {
 
   drupal_alter('privatemsg_message_presave', $message);
 
-  $index_sql = "INSERT INTO {pm_index} (mid, thread_id, uid, is_new, deleted) VALUES (%d, %d, %d, %d, 0)";
+  $index_sql = "INSERT INTO {pm_index} (mid, thread_id, recipient, type, is_new, deleted) VALUES (%d, %d, %d, '%s', %d, 0)";
   if (isset($message['read_all']) && $message['read_all']) {
     // The message was sent in read all mode, add the author as recipient to all
     // existing messages.
     $query_messages = _privatemsg_assemble_query('messages', array($message['thread_id']), NULL);
     $conversation = db_query($query_messages['query']);
     while ($result = db_fetch_array($conversation)) {
-      if (!db_query($index_sql, $result['mid'], $message['thread_id'], $message['author']->uid, 0)) {
+      if (!db_query($index_sql, $result['mid'], $message['thread_id'], $message['author']->uid, 'user', 0)) {
         return FALSE;
       }
     }
@@ -1421,7 +1531,11 @@ function _privatemsg_send($message) {
   // 2) Save message to recipients.
   // Each recipient gets a record in the pm_index table.
   foreach ($message['recipients'] as $recipient) {
-    if (!db_query($index_sql, $mid, $message['thread_id'], $recipient->uid, 1) ) {
+    if (!isset($recipient->type)) {
+      $recipient->type = 'user';
+      $recipient->key = 'uid';
+    }
+    if (!db_query($index_sql, $mid, $message['thread_id'], $recipient->{$recipient->key}, $recipient->type, 1) ) {
       // We assume if one insert failed then the rest may fail too against the
       // same table.
       return FALSE;
@@ -1433,7 +1547,7 @@ function _privatemsg_send($message) {
   $is_new = isset($message['recipients'][$message['author']->uid]) ? 1 : 0;
 
   // Also add a record for the author to the pm_index table.
-  if (!db_query($index_sql, $mid, $message['thread_id'], $message['author']->uid, $is_new)) {
+  if (!db_query($index_sql, $mid, $message['thread_id'], $message['author']->uid, 'user', $is_new)) {
     return FALSE;
   }
 
@@ -1712,7 +1826,7 @@ function privatemsg_thread_change_status
   }
   // Merge status and uid with the threads list. array_merge() will not overwrite/ignore thread_id 1.
   $params = array_merge(array($status, $account->uid), $threads);
-  db_query('UPDATE {pm_index} SET is_new = %d WHERE uid = %d AND thread_id IN ('. db_placeholders($threads) .')', $params);
+  db_query("UPDATE {pm_index} SET is_new = %d WHERE recipient = %d and type = 'user' AND thread_id IN (" . db_placeholders($threads) . ')', $params);
 
   if ($status == PRIVATEMSG_UNREAD) {
     drupal_set_message(format_plural(count($threads), 'Marked 1 thread as unread.', 'Marked @count threads as unread.'));
@@ -1815,15 +1929,23 @@ function privatemsg_thread_change_delete
 function privatemsg_privatemsg_block_message($author, $recipients) {
   $blocked = array();
   if (privatemsg_is_disabled($author)) {
-    $blocked[] = array('uid' => $author->uid,
-                       'message' => t('You have disabled private message sending and receiving.'),
-                 );
+    $blocked[] = array(
+      'uid' => $author->uid,
+      'message' => t('You have disabled private message sending and receiving.'),
+    );
   }
   foreach($recipients as $recipient) {
     if (privatemsg_is_disabled($recipient)) {
-      $blocked[] = array('uid' => $recipient->uid,
-                       'message' => t('%recipient has disabled private message receiving.', array('%recipient' => $recipient->name))
-                 );
+      $blocked[] = array(
+        'uid' => $recipient->uid,
+        'message' => t('%recipient has disabled private message receiving.', array('%recipient' => $recipient->name)),
+      );
+    }
+    if (isset($recipient->type) && $recipient->type == 'role' && !privatemsg_user_access('write privatemg to roles')) {
+      $blocked[] = array(
+        'uid' => $recipient->recipient,
+        'message' => t('Not allowed to write private messages to role members'),
+      );
     }
   }
 
@@ -1921,4 +2043,63 @@ function privatemsg_views_api() {
     'api' => 2,
     'path' => drupal_get_path('module', 'privatemsg') . '/views',
   );
+}
+
+/**
+ * Implements hook_privatemsg_recipient_autocomplete().
+ */
+function privatemsg_privatemsg_recipient_autocomplete($fragment, $names, $limit) {
+  $query = _privatemsg_assemble_query('autocomplete_roles', $fragment, $names);
+  $result = db_query_range($query['query'], $fragment, 0, $limit);
+  $prefix = count($names) ? implode(", ", $names) .", " : '';
+  // 3: Build proper suggestions and print.
+  $matches = array();
+  while ($role = db_fetch_object($result)) {
+    $matches[$prefix . $role->name .", "] = t('Role %role', array('%role' => $role->name));
+  }
+  return $matches;
+}
+
+/**
+ * Implements hook_privatemsg_name_lookup().
+ */
+function privatemsg_privatemsg_name_lookup($string) {
+  $result = db_query("SELECT * FROM {role} WHERE name = '%s'", $string);
+  if ($role = db_fetch_object($result)) {
+    $role->type = 'role';
+    $role->key = 'rid';
+    return $role;
+  }
+}
+
+/**
+ * Implements hook_privatemsg_recipients_where().
+ */
+function privatemsg_privatemsg_recipients_where($account) {
+  return array(
+    'where' => array("pmi.type = 'role' AND pmi.recipient IN (" . db_placeholders(array_keys($account->roles)) . ")"),
+    'args'  => array_keys($account->roles),
+  );
+}
+
+/**
+ * Implements hook_privatemgs_sql_participants_alter().
+ */
+function privatemsg_privatemsg_sql_participants_alter(&$fragments, $thread_id) {
+  $fragments['select'][] = 'r.name AS role_name';
+  $fragments['select'][] = "'rid' AS role_key";
+
+  $fragments['inner_join'][] = "LEFT JOIN {role} r ON (r.rid = pmi.recipient AND pmi.type = 'role')";
+  $fragments['group_by'][]    = 'role_name';
+}
+
+/**
+ * Implements hook_privatemsg_is_recipient().
+ */
+function privatemsg_privatemsg_is_recipient($account, $participants) {
+  foreach ($account->roles as $role_id => $role_name) {
+    if (isset($participants['role_' . $role_id])) {
+      return TRUE;
+    }
+  }
 }
\ No newline at end of file
Index: privatemsg.pages.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.pages.inc,v
retrieving revision 1.1
diff -u -p -r1.1 privatemsg.pages.inc
--- privatemsg.pages.inc	2 Dec 2009 20:03:59 -0000	1.1
+++ privatemsg.pages.inc	15 Jan 2010 13:38:47 -0000
@@ -200,15 +200,15 @@ function privatemsg_new(&$form_state, $r
       continue;
     }
     // Check if another module is blocking the sending of messages to the recipient by current user.
-    $user_blocked = module_invoke_all('privatemsg_block_message', $user, array($recipient->uid => $recipient));
-    if (!count($user_blocked) <> 0 && $recipient->uid) {
-      if ($recipient->uid == $user->uid) {
+    $user_blocked = module_invoke_all('privatemsg_block_message', $user, array($recipient->recipient => $recipient));
+    if (!count($user_blocked) <> 0 && $recipient->recipient) {
+      if ($recipient->recipient == $user->uid) {
         $usercount++;
         // Skip putting author in the recipients list for now.
         continue;
       }
       $to[] = $recipient->name;
-      $to_themed[$recipient->uid] = theme('username', $recipient);
+      $to_themed[$recipient->type . '_' . $recipient->recipient] = _privatemsg_format_participant($recipient);
     }
     else {
       // Recipient list contains blocked users.
@@ -219,7 +219,7 @@ function privatemsg_new(&$form_state, $r
   if (empty($to) && $usercount >= 1 && !$blocked) {
     // Assume the user sent message to own account as if the usercount is one or less, then the user sent a message but not to self.
     $to[] = $user->name;
-    $to_themed[$user->uid] = theme('username', $user);
+    $to_themed[$user->uid] = _privatemsg_format_participant($user);
   }
 
   if (!empty($to)) {
@@ -363,8 +363,8 @@ function privatemsg_new_validate($form, 
     // Load participants.
     $message['recipients'] = _privatemsg_load_thread_participants($message['thread_id']);
     // Remove author.
-    if (isset($message['recipients'][$message['author']->uid]) && count($message['recipients']) > 1) {
-      unset($message['recipients'][$message['author']->uid]);
+    if (isset($message['recipients']['user_' . $message['author']->uid]) && count($message['recipients']) > 1) {
+      unset($message['recipients']['user_' . $message['author']->uid]);
     }
   }
 
@@ -387,7 +387,12 @@ function privatemsg_new_submit($form, &$
   // Load usernames to which the message was sent to.
   $recipient_names = array();
   foreach ($form_state['validate_built_message']['recipients'] as $recipient) {
-    $recipient_names[] = theme('username', $recipient);
+    if (isset($recipient->uid)) {
+      $recipient_names[] = _privatemsg_format_participant($recipient);
+    }
+    else {
+      $recipient_names[] = $recipient->name;
+    }
   }
   if ($status !== FALSE )  {
     drupal_set_message(t('A message has been sent to !recipients.', array('!recipients' => implode(', ', $recipient_names))));
@@ -641,17 +646,26 @@ function privatemsg_user_name_autocomple
       $names[$name] = $name;
     }
   }
-  // By using user_validate_user we can ensure that names included in $names are at least logisticaly possible.
   // 2: Find the next user name suggestion.
   $fragment = array_pop($names);
   $matches = array();
   if (!empty($fragment)) {
-    $query = _privatemsg_assemble_query('autocomplete', $fragment, $names);
-    $result = db_query_range($query['query'], $fragment, 0, 10);
-    $prefix = count($names) ? implode(", ", $names) .", " : '';
-    // 3: Build proper suggestions and print.
-    while ($user = db_fetch_object($result)) {
-      $matches[$prefix . $user->name .", "] = $user->name;
+    $remaining = 10;
+    foreach (module_implements('privatemsg_recipient_autocomplete') as $module) {
+      $matches += module_invoke($module, 'privatemsg_recipient_autocomplete', $fragment, $names, $remaining);
+      $remaining = 10 - count($matches);
+      if ($remaining <= 0) {
+        break;
+      }
+    }
+    if ($remaining > 0) {
+      $query = _privatemsg_assemble_query('autocomplete', $fragment, $names);
+      $result = db_query_range($query['query'], $fragment, 0, $remaining);
+      $prefix = count($names) ? implode(", ", $names) .", " : '';
+      // 3: Build proper suggestions and print.
+      while ($user = db_fetch_object($result)) {
+        $matches[$prefix . $user->name .", "] = $user->name;
+      }
     }
   }
   // convert to object to prevent drupal bug, see http://drupal.org/node/175361
Index: privatemsg.test
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.test,v
retrieving revision 1.6
diff -u -p -r1.6 privatemsg.test
--- privatemsg.test	2 Jan 2010 12:11:09 -0000	1.6
+++ privatemsg.test	15 Jan 2010 13:38:47 -0000
@@ -304,7 +304,7 @@ class PrivatemsgTestCase extends DrupalW
     );
 
     $this->drupalPost(NULL, $reply, t('Send message'));
-    $this->assertText(t('A message has been sent to @recipient, @author.', array('@recipient' => $recipient->name, '@author' => $author->name)), 'Reply has been sent.');
+    $this->assertText(t('A message has been sent to @author, @recipient.', array('@recipient' => $recipient->name, '@author' => $author->name)), 'Reply has been sent.');
     $this->assertText($reply['body'], 'New message body displayed.');
 
     $this->drupalPost(NULL, $replyempty, t('Send message'));
@@ -450,7 +450,7 @@ class PrivatemsgTestCase extends DrupalW
     $this->assertText($edit['body'], t('First message body displayed.'));
     $this->assertText($admin_edit['body'], t('New message body displayed.'));
 
-    $admin_recipient_count = db_result(db_query("SELECT COUNT(*) FROM {pm_index} WHERE uid = %d AND thread_id = %d", $admin->uid, 1));
+    $admin_recipient_count = db_result(db_query("SELECT COUNT(*) FROM {pm_index} WHERE recipient = %d AND thread_id = %d", $admin->uid, 1));
     $this->assertEqual($admin_recipient_count, 2, t('Admin is listed as recipient for every message once.'));
 
 
@@ -465,7 +465,7 @@ class PrivatemsgTestCase extends DrupalW
     $this->assertText($admin_edit['body'], t('Second response body displayed.'));
     $this->assertText($admin_edit2['body'], t('Third message body displayed.'));
 
-    $admin_recipient_count = db_result(db_query("SELECT COUNT(*) FROM {pm_index} WHERE uid = %d AND thread_id = %d", $admin->uid, 1));
+    $admin_recipient_count = db_result(db_query("SELECT COUNT(*) FROM {pm_index} WHERE recipient = %d AND thread_id = %d", $admin->uid, 1));
     $this->assertEqual($admin_recipient_count, 3, t('Admin is listed as recipient for every message once.'));
 
   }
Index: pm_block_user/pm_block_user.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/pm_block_user/pm_block_user.module,v
retrieving revision 1.3
diff -u -p -r1.3 pm_block_user.module
--- pm_block_user/pm_block_user.module	2 Dec 2009 20:04:00 -0000	1.3
+++ pm_block_user/pm_block_user.module	15 Jan 2010 13:38:48 -0000
@@ -181,7 +181,7 @@ function pm_block_user_privatemsg_block_
 function pm_block_user_privatemsg_sql_load_alter(&$fragments, $pmid, $uid) {
   $fragments['select'][] = 'pmbu.recipient AS is_blocked';
 
-  $fragments['inner_join'][] = 'LEFT JOIN {pm_block_user} pmbu ON (pm.author = pmbu.author AND pmi.uid = pmbu.recipient)';
+  $fragments['inner_join'][] = "LEFT JOIN {pm_block_user} pmbu ON (pm.author = pmbu.author AND pmi.recipient = pmbu.recipient AND pmi.type = 'user')";
 }
 
 /**
Index: pm_email_notify/pm_email_notify.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/pm_email_notify/pm_email_notify.module,v
retrieving revision 1.3
diff -u -p -r1.3 pm_email_notify.module
--- pm_email_notify/pm_email_notify.module	2 Dec 2009 20:04:00 -0000	1.3
+++ pm_email_notify/pm_email_notify.module	15 Jan 2010 13:38:48 -0000
@@ -52,7 +52,7 @@ function _pm_email_notify_is_enabled($ui
 function pm_email_notify_privatemsg_message_insert($message) {
   foreach ($message['recipients'] as $recipient) {
     // check if recipient enabled email notifications
-    if (_pm_email_notify_is_enabled($recipient->uid)) {
+    if (isset($recipient->uid) && _pm_email_notify_is_enabled($recipient->uid)) {
       // send them a new pm notification email if they did
       $params['recipient'] = $recipient;
       $params['message'] = $message;
Index: privatemsg_filter/privatemsg_filter.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg_filter/privatemsg_filter.module,v
retrieving revision 1.3
diff -u -p -r1.3 privatemsg_filter.module
--- privatemsg_filter/privatemsg_filter.module	2 Dec 2009 20:04:00 -0000	1.3
+++ privatemsg_filter/privatemsg_filter.module	15 Jan 2010 13:38:48 -0000
@@ -589,7 +589,7 @@ function privatemsg_filter_privatemsg_sq
     $count = 0;
     if (isset($filter['tags']) && !empty($filter['tags'])) {
       foreach ($filter['tags'] as $tag) {
-        $fragments['inner_join'][]  = "INNER JOIN {pm_tags_index} pmti$count ON (pmti$count.thread_id = pmi.thread_id AND pmti$count.uid = pmi.uid)";
+        $fragments['inner_join'][]  = "INNER JOIN {pm_tags_index} pmti$count ON (pmti$count.thread_id = pmi.thread_id AND pmti$count.uid = pmi.recipient AND pmi.type = 'user')";
         $fragments['where'][]       = "pmti$count.tag_id = %d";
         $fragments['query_args']['where'][]   = $tag;
         $count++;
@@ -599,7 +599,7 @@ function privatemsg_filter_privatemsg_sq
     if (isset($filter['author']) && !empty($filter['author'])) {
       foreach ($filter['author'] as $author) {
         $fragments['inner_join'][]  = "INNER JOIN {pm_index} pmi$count ON (pmi$count.mid = pm.mid)";
-        $fragments['where'][]       = "pmi$count.uid = %d";
+        $fragments['where'][]       = "pmi$count.recipient = %d AND type = 'user'";
         $fragments['query_args']['where'][]   = $author->uid;
         $count++;
       }
@@ -752,10 +752,10 @@ function privatemsg_filter_privatemsg_sq
   // @todo: Check if these results can be grouped to avoid unecessary loops.
   if (arg(1) == 'filter') {
     // JOIN on index entries where the to be selected user is a recipient.
-    $fragments['inner_join'][] = 'INNER JOIN {pm_index} pip ON pip.uid = u.uid';
+    $fragments['inner_join'][] = "INNER JOIN {pm_index} pip ON pip.recipient = u.uid AND pip.type = 'user'";
     // JOIN on rows where the current user is the recipient and that have the
     // same mid as those above.
-    $fragments['inner_join'][] = 'INNER JOIN {pm_index} piu ON piu.uid = %d AND pip.mid = piu.mid';
+    $fragments['inner_join'][] = "INNER JOIN {pm_index} piu ON piu.recipient = u.uid AND piu.type = 'user' AND pip.mid = piu.mid";
     $fragments['query_args']['join'][] = $user->uid;
   }
 }
