? .cvsignore
? privatemsg.list_messages.patch
? privatemsg.list_messages2.patch
? privatemsg.list_messages3.patch
? translations
? undo.ogv
Index: privatemsg.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.module,v
retrieving revision 1.70.2.30.2.91.2.14
diff -u -p -r1.70.2.30.2.91.2.14 privatemsg.module
--- privatemsg.module	8 Feb 2009 18:13:06 -0000	1.70.2.30.2.91.2.14
+++ privatemsg.module	9 Feb 2009 15:22:12 -0000
@@ -83,28 +83,23 @@ function privatemsg_menu() {
   $items['messages'] = array(
     'title'            => 'Messages',
     'title callback'  => 'privatemsg_title_callback',
-    'page callback'    => 'privatemsg_list',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('privatemsg_list', 'list'),
     'access arguments' => array('read privatemsg'),
     'type'             => MENU_NORMAL_ITEM,
   );
   $items['messages/inbox'] = array(
     'title'            => 'All messages',
-    'page callback'    => 'privatemsg_list',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('privatemsg_list', 'list'),
     'access arguments' => array('read privatemsg'),
     'type'             => MENU_DEFAULT_LOCAL_TASK,
     'weight'           => -10,
   );
-  $items['messages/mark-read'] = array(
-    'title'            => 'Mark all as read',
-    'page callback'    => 'privatemsg_set_new_status',
-    'page arguments'   => array(NULL, NULL, 0, TRUE),
-    'access callback'  => 'privatemsg_unread_count',
-    'type'             => MENU_NORMAL_ITEM,
-    'weight'           => -9,
-  );
   $items['messages/sent'] = array(
     'title'            => 'Sent messages',
-    'page callback'    => 'privatemsg_list',
+    'page callback'    => 'drupal_get_form',
+    'page arguments'   => array('privatemsg_list', 'list_sent'),
     'access arguments' => array('read privatemsg'),
     'type'             => MENU_LOCAL_TASK,
     'weight'           => -5,
@@ -148,6 +143,13 @@ function privatemsg_menu() {
     'access arguments' => array('administer privatemsg settings'),
     'type'             => MENU_NORMAL_ITEM,
   );
+  $items['messages/undo/action'] = array(
+    'title'            => 'Private messages',
+    'description'      => 'Undo last thread action',
+    'page callback'    => 'privatemsg_undo_action',
+    'access arguments' => array('read privatemsg'),
+    'type'             => MENU_CALLBACK,
+  );
   return $items;
 }
 /**
@@ -240,8 +242,8 @@ function privatemsg_theme() {
       'arguments'        => array('recipients' => NULL),
       'template'         => 'privatemsg-between',
     ),
-    'privatemsg_message_row'  => array(
-      'arguments'             => array('row'),
+    'privatemsg_list_messages'  => array(
+      'arguments'             => array('form'),
     ),
     'privatemsg_new_block'  => array(
       'arguments'             => array('count'),
@@ -278,13 +280,13 @@ function privatemsg_preprocess_privatems
  *
  * @param $uid - user id for whom to load messages.
  */
-function privatemsg_list($uid = NULL) {
+function privatemsg_list(&$form_state, $query_name = 'list', $uid = NULL) {
   global $user;
   disallow_anon_access();
 
   // Setting default behavior...
   $account = $user;
-  if (is_int($uid) && $uid != $user->uid) {
+  if ((int)$uid > 0 && $uid != $user->uid) {
     // Trying to view someone else's messages...
     if (!user_access('read all private messages')) {
       drupal_set_message(t("You do not have sufficient rights to view someone else's messages"), 'warning');
@@ -296,99 +298,278 @@ function privatemsg_list($uid = NULL) {
   }
   // By this point we have figured out for which user we are listing messages and now it is safe to use $account->uid in the listing query.
 
-  $content = array();
-  $content['list']['content'] = privatemsg_list_messages($account);
-  $content['list']['#weight'] = 0;
+  drupal_add_css(drupal_get_path('module', 'privatemsg') .'/styles/privatemsg-list.css');
 
-  drupal_alter('privatemsg_list_messages', $content, $account);
+  $query = _privatemsg_assemble_query($query_name, $account);
+  $result = pager_query($query['query'], variable_get('privatemsg_per_page', 25), 0, $query['count']);
 
-  uasort($content, 'element_sort');
+  $threads = array();
+  $form['#rows'] = array();
+  while ($thread = db_fetch_array($result)) {
+    $form['#rows'][] = $thread;
+    $threads[$thread['thread_id']] = '';
+  }
 
-  $expand = '';
-  foreach ($content as $element) {
-    $expand .= $element['content'];
+  if (!empty($form['#rows'])) {
+    $form['actions'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Actions'),
+      '#prefix' => '<div class="container-inline">',
+      '#suffix' => '</div>',
+      '#collapsible' => TRUE,
+      '#collapsed' => FALSE,
+      '#weight' => 15,
+    );
+
+    $form['actions']['delete'] = array(
+      '#type'  => 'submit',
+      '#value' => t('Delete'),
+      '#submit' => array('privatemsg_list_submit_delete'),
+    );
+
+    $options = array(0 => t('More actions..'));
+    foreach (module_invoke_all('privatemsg_thread_operations') as $operation => $array) {
+      $options[$operation] = $array['label'];
+    }
+    $form['actions']['operation'] = array(
+      '#type' => 'select',
+      '#options' => $options,
+      '#default_value' => 0,
+      '#attributes' => array('onchange' => "$('#edit-submit').click()"),
+    );
+    $form['actions']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Execute'),
+      '#submit' => array('privatemsg_list_submit'),
+    );
   }
-  return $expand;
+
+  $form['threads'] = array('#type' => 'checkboxes', '#options' => $threads);
+  $form['pager'] = array('#value' => theme('pager'), '#weight' => 20);
+  $form['#theme'] = 'privatemsg_list_messages';
+  $form['#account'] = $account;
+
+  return $form;
 }
 
 /**
- * Display the list of messages for a given user, called by privatemsg_privatemsg_alter
+ * Implementation of hook_privatemsg_thread_operations().
  */
-function privatemsg_list_messages($account) {
-  switch (arg(1)) {
-    case 'sent':
-      $query = _privatemsg_assemble_query('list_sent', $account);
-      break;
-    case 'inbox':
-    default:
-      $query = _privatemsg_assemble_query('list', $account);
-  }
-  $result = pager_query($query['query'], variable_get('privatemsg_per_page', 25), 0, $query['count']);
+function privatemsg_privatemsg_thread_operations() {
+  $operations = array(
+    'mark as read' => array(
+      'label' => t('Mark as read'),
+      'callback' => '_privatemsg_thread_setnew',
+      'callback arguments' => array('new' => 0),
+      'undo callback' => '_privatemsg_thread_setnew',
+      'undo callback arguments' => array('new' => 1),
+    ),
+    'mark as unread' => array(
+      'label' => t('Mark as unread'),
+      'callback' => '_privatemsg_thread_setnew',
+      'callback arguments' => array('new' => 1),
+      'undo callback' => '_privatemsg_thread_setnew',
+      'undo callback arguments' => array('new' => 0),
+    ),
+  );
+  return $operations;
+}
 
-  $rows = array();
-  while ($thread = db_fetch_array($result)) {
-    $rows[] = theme('privatemsg_message_row', $thread);
+/**
+ * Menu callback for messages/undo/action.
+ *
+ * This function will test if a undo callback is stored in SESSION and execute it.
+ */
+function privatemsg_undo_action() {
+  if (isset($_SESSION['privatemsg']['undo callback']) && is_array($_SESSION['privatemsg']['undo callback'])) {
+    $undo = $_SESSION['privatemsg']['undo callback'];
+    if (isset($undo['function']) && isset($undo['args'])) {
+      call_user_func_array($undo['function'], $undo['args']);
+    }
+    drupal_goto();
   }
+}
 
-  $content = '';
-  if (!empty($rows)) {
-    $head = array();
-    foreach (array_keys($rows[0]) as $index) {
-      $head[$index] =  array('data' => $index);
-      if ($index == t('Last updated') || $index == t('Subject')) {
-        $head[$index] += array('field' => $index, 'sort' => 'desc');
-      }
-    }
+/**
+ * Process privatemsg_list form submissions.
+ *
+ * Execute the chosen action on the selected messages. This function is
+ * a copy of node_admin_nodes_submit
+ */
+function privatemsg_list_submit($form, &$form_state) {
+  $operations = module_invoke_all('privatemsg_thread_operations');
+  $operation = $operations[$form_state['values']['operation']];
+  // Filter out unchecked nodes
+  $threads = array_filter($form_state['values']['threads']);
+  if ($operation['callback']) {
+    _privatemsg_execute_action($threads, $operation);
+  }
+  else {
+    // We need to rebuild the form to go to a second step.  For example, to
+    // show the confirmation form for the deletion of privatemsg.
+    $form_state['rebuild'] = TRUE;
+  }
+}
 
-    $content = theme('table', $head, $rows);
-    $content .= theme('pager');
-    drupal_add_css(drupal_get_path('module', 'privatemsg') .'/styles/privatemsg-list.css');
+/**
+ * Executes the action, helper function for privatemsg_list_submit
+ *
+ * @param array $threads   Array with thread id's to work on
+ * @param array $operation Operation array, @see privatemsg_privatemsg_thread_operations
+ */
+function _privatemsg_execute_action($threads, $operation) {
+  // Add in callback arguments if present.
+  if (isset($operation['callback arguments'])) {
+    $args = array_merge(array($threads), $operation['callback arguments']);
   }
   else {
-    drupal_set_message(t('No messages to display.'));
+    $args = array($threads);
   }
+  call_user_func_array($operation['callback'], $args);
+  
+  if (isset($operation['undo callback']) && $undo_function = $operation['undo callback']) {
+    // Add in callback arguments if present.
+    if (isset($operation['undo callback arguments'])) {
+      $undo_args = array_merge(array($threads), $operation['undo callback arguments']);
+    }
+    else {
+      $undo_args = array($threads);
+    }
+    $_SESSION['privatemsg']['undo callback'] = array('function' => $undo_function,
+                                                     'args'     => $undo_args);
+    $undo = l(t('undone'), 'messages/undo/action', array('query' => drupal_get_destination()));
 
-  return $content;
+    drupal_set_message(t('The previous action can be !undo.', array('!undo' => $undo)));
+  }
 }
 
-function theme_privatemsg_new_block($count) {
-  $text = format_plural($count, 'You have a new message, click here to read it',
-                        'You have @count new messages, click here to read them',
-                        array('@count' => $count));
+/**
+ * Submit callback for the delete button
+ *
+ * @param array $form
+ * @param array $form_state
+ */
+function privatemsg_list_submit_delete($form, &$form_state) {
+  global $user;
 
-  return l($text, 'messages', array('attributes' => array('id' => 'privatemsg-new-link')));
+  $operation = array(
+      'callback' => 'privatemsg_thread_delete',
+      'callback arguments' => array('delete' => 1),
+      'undo callback' => 'privatemsg_thread_delete',
+      'undo callback arguments' => array('delete' => 0),
+  );
+  _privatemsg_execute_action(array_filter($form_state['values']['threads']), $operation);
 }
 
 /**
- * Theme a row of messages
+ * Delete or restore one or multiple threads
  *
+ * @param array|int $threads Array with id's, a single int value will be converted to int
+ * @param int       $delete  Indiciates if the threads should be deleted or restored. 1 = delete, 0 => restore
+ * @param object    $account User object for which the threads should be deleted, defaults to the current user
  */
-function theme_privatemsg_message_row($row) {
-  $themed_row = array();
+function privatemsg_thread_delete($threads, $delete = 1, $account = NULL) {
+  if (!is_array($threads)) {
+    $threads = array($threads);
+  }
+  
+  if (empty($account)) {
+    global $user;
+    $account = drupal_clone($user);
+  }
+
+  $params = array($delete, $account->uid) + $threads;
 
-  $unread = '';
-  if (isset($row['is_new']) && $row['is_new'] ) {
-    $unread = ' privatemsg-unread';
+  db_query('UPDATE {pm_index} SET deleted = %d WHERE uid = %d AND thread_id IN ('. db_placeholders($threads) .')', $params);
+  if ($delete) {
+    drupal_set_message(t('Deleted %count threads.', array('%count' => count($threads))));
   }
-  $themed_row[t('Subject')] = '<span class="privatemsg-list-subject'. $unread .'">'. l($row['subject'], 'messages/view/'. $row['thread_id']) .'</span>';
-  if (isset($row['author']) && $row['author']) {
-    $authors = _privatemsg_generate_user_array($row['author']);
-    $themed_row[t('Authors')] = '<span class="privatemsg-list-from'.  $unread .'">'. _privatemsg_format_participants($authors, 3, TRUE) .'</span>';
+  else {
+    drupal_set_message(t('Restored %count threads.', array('%count' => count($threads))));
+  }
+}
+
+/**
+ * Marks one or multiple threads as (un)read
+ *
+ * @param array|int $threads Array with id's, a single int value will be converted to int
+ * @param int       $is_new  Indiciates if the threads should be marked as read or unread. 1 = unread, 0 => read
+ * @param object    $account User object for which the threads should be deleted, defaults to the current user
+ */
+function privatemsg_thread_read($threads, $is_new = 0, $account = NULL) {
+  if (!is_array($threads)) {
+    $threads = array($threads);
   }
-  if (array_key_exists('recipient', $row)) {
-    $themed_row[t('Recipients')] = '';
-    if (!empty($row['recipient'])) {
-      $recipients = _privatemsg_generate_user_array($row['recipient']);
-      $themed_row[t('Recipients')] = '<span class="privatemsg-list-to'.  $unread .'">'. _privatemsg_format_participants($recipients, 3, TRUE) .'</span>';
+
+  if (empty($account)) {
+    global $user;
+    $account = drupal_clone($user);
+  }
+
+  $params = array($is_new, $account->uid) + $threads;
+  db_query('UPDATE {pm_index} SET is_new = %d WHERE uid = %d AND thread_id IN ('. db_placeholders($threads) .')', $params);
+
+  if ($is_new) {
+    drupal_set_message(t('Marked %count threads as unread.', array('%count' => count($threads))));
+  }
+  else {
+    drupal_set_message(t('Marked %count threads as read.', array('%count' => count($threads))));
+  }
+}
+
+/**
+ * Theme to display the privatemsg list
+ */
+function theme_privatemsg_list_messages($form) {
+    // If there are rows in this form, then $form['title'] contains a list of
+  // the title form elements.
+  $has_posts = !empty($form['#rows']);
+  $select_header = $has_posts ? theme('table_select_header_cell') : '';
+  $header = array($select_header, t('Subject'), t('Authors'), t('Last Updated'));
+
+  $themed_rows = array();
+  foreach ($form['#rows'] as $row) {
+    $themed_row = array();
+    $themed_row[] = drupal_render($form['threads'][$row['thread_id']]);
+
+    $unread = '';
+    if (isset($row['is_new']) && $row['is_new'] ) {
+      $unread = ' privatemsg-unread';
+    }
+    $themed_row[t('Subject')] = '<span class="privatemsg-list-subject'. $unread .'">'. l($row['subject'], 'messages/view/'. $row['thread_id']) .'</span>';
+    if (isset($row['author']) && $row['author']) {
+      $authors = _privatemsg_generate_user_array($row['author']);
+      $themed_row[t('Authors')] = '<span class="privatemsg-list-from'.  $unread .'">'. _privatemsg_format_participants($authors, 3, TRUE) .'</span>';
     }
+    if (array_key_exists('recipient', $row)) {
+      $themed_row[t('Recipients')] = '';
+      if (!empty($row['recipient'])) {
+        $recipients = _privatemsg_generate_user_array($row['recipient']);
+        $themed_row[t('Recipients')] = '<span class="privatemsg-list-to'.  $unread .'">'. _privatemsg_format_participants($recipients, 3, TRUE) .'</span>';
+      }
+    }
+    if ($row['last_updated']) {
+      $themed_row[t('Last updated')] = '<span class="privatemsg-list-date'.  $unread .'">'. format_date($row['last_updated'], 'small') .'</span>';
+    }
+    $themed_rows[] = $themed_row;
   }
-//  drupal_set_message('<pre>'. print_r($row, 1) . '</pre>');
-  if ($row['last_updated']) {
-    $themed_row[t('Last updated')] = '<span class="privatemsg-list-date'.  $unread .'">'. format_date($row['last_updated'], 'small') .'</span>';
+  unset($form['rows']);
+
+  if (empty($themed_rows)) {
+    $themed_rows[] = array(array('data' => t('No messages available.'), 'colspan' => count($header)));
   }
-  return $themed_row;
+
+  $form['list'] = array('#value' => theme('table', $header, $themed_rows), '#weight' => 5);
+  return drupal_render($form);
 }
 
+function theme_privatemsg_new_block($count) {
+  $text = format_plural($count, 'You have a new message, click here to read it',
+                        'You have @count new messages, click here to read them',
+                        array('@count' => $count));
+
+  return l($text, 'messages', array('attributes' => array('id' => 'privatemsg-new-link')));
+}
 /**
  * API function
  *
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.1
diff -u -p -r1.1 pm_block_user.module
--- pm_block_user/pm_block_user.module	5 Nov 2008 05:29:47 -0000	1.1
+++ pm_block_user/pm_block_user.module	9 Feb 2009 15:22:12 -0000
@@ -74,11 +74,11 @@ function pm_block_user_form_submit($form
   if ($form_state['values']['confirm']) {
     switch ($form_state['values']['block_action']) {
       case 'block_user':
-        db_query('INSERT INTO {pm_block_user} (author, recipient) VALUES (%d, %d)', $form_state['values']['author'], $form_state['values']['recipient']);
+        pm_block_user_blockauthor($form_state['values']['author'], $form_state['values']['recipient']);
         drupal_set_message(t('@author has been blocked from sending you any further messages.', array('@author' => $form_state['values']['author_name'])));
       break;
       case 'unblock_user':
-        db_query('DELETE FROM {pm_block_user} WHERE author = %d AND recipient = %d', $form_state['values']['author'], $form_state['values']['recipient']);
+        pm_block_user_blockauthor($form_state['values']['author'], $form_state['values']['recipient'], 0);
         drupal_set_message(t('@author is now allowed to send you new messages.', array('@author' => $form_state['values']['author_name'])));
       break;
     }
@@ -116,4 +116,75 @@ function pm_block_user_form_submit($form
   if (isset($output)) {
     return $output;
   }
+}
+
+function pm_block_user_privatemsg_thread_operations() {
+  $operations = array(
+    'block' => array(
+      'label' => t('Block all authors'),
+      'callback' => 'pm_block_user_thread_block',
+      'callback arguments' => array('block' => 1),
+      'undo callback' => 'pm_block_user_thread_block',
+      'undo callback arguments' => array('block' => 0),
+    ),
+    'unblock' => array(
+      'label' => t('Unblock all authors'),
+      'callback' => 'pm_block_user_thread_block',
+      'callback arguments' => array('block' => 0),
+      'undo callback' => 'pm_block_user_thread_block',
+      'undo callback arguments' => array('block' => 1),
+    ),
+  );
+  return $operations;
+}
+
+function pm_block_user_thread_block($threads, $block = 1, $user = NULL) {
+  if (!is_array($threads)) {
+    $threads = array($threads);
+  }
+
+  if (empty($account)) {
+    global $user;
+    $account = drupal_clone($user);
+  }
+
+  $query = _privatemsg_assemble_query(array('threadauthors', 'pm_block_user'), $threads);
+  $result = db_query($query['query']);
+  $count = 0;
+  while ($author = db_fetch_array($result)) {
+    if ($author['uid'] != $user->uid) {
+      $count++;
+      pm_block_user_blockauthor($author['uid'], $user->uid, $block);
+    }
+  }
+  
+  if ($block) {
+    drupal_set_message(t('Blocked %count authors.', array('%count' => $count)));
+  }
+  else {
+    drupal_set_message(t('Unblocked %count authors.', array('%count' => $count)));
+  }
+}
+
+function pm_block_user_sql_threadauthors(&$fragments, $threads) {
+  $fragments['primary_table'] = '{pm_message} pm';
+
+  $fragments['select'][] = 'pm.author as uid';
+
+  $fragments['inner_join'][] = 'INNER JOIN {pm_index} pmi ON (pmi.mid = pm.mid)';
+
+  $fragments['where'][] = 'pmi.thread_id IN ('. db_placeholders($threads) .')';
+  $fragments['query_args'] += $threads;
+
+  $fragments['group_by'][] = 'pm.author';
+}
+
+function pm_block_user_blockauthor($author_id, $recipient_id, $block = 1) {
+  if ($block) {
+    db_query('INSERT INTO {pm_block_user} (author, recipient) VALUES (%d, %d)', $author_id, $recipient_id);
+  }
+  else {
+    db_query('DELETE FROM {pm_block_user} WHERE author = %d AND recipient = %d', $author_id, $recipient_id);
+  }
+  return true;
 }
\ No newline at end of file
Index: privatemsg_filter/privatemsg_filter.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg_filter/privatemsg_filter.module,v
retrieving revision 1.1.2.4
diff -u -p -r1.1.2.4 privatemsg_filter.module
--- privatemsg_filter/privatemsg_filter.module	8 Feb 2009 18:13:06 -0000	1.1.2.4
+++ privatemsg_filter/privatemsg_filter.module	9 Feb 2009 15:22:12 -0000
@@ -156,7 +156,7 @@ function privatemsg_filter_delete_tags_s
 function privatemsg_filter_get_filter($account) {
   $filter = array();
   if (isset($_GET['tags'])) {
-    $tag_data = privatemsg_filter_get_tags_data($account);
+    $tag_data = privatemsg_filter_get_tags_data($account->uid);
     foreach (explode(' ', $_GET['tags']) as $tag) {
       if (isset($tag_data[$tag])) {
         $filter['tags'][$tag] = $tag;
@@ -183,7 +183,7 @@ function privatemsg_filter_get_filter($a
     $filter['search'] = $_GET['search'];
   }
 
-  if(!empty($filter)) {
+  if (!empty($filter)) {
     return $filter;
   }
 
@@ -194,7 +194,7 @@ function privatemsg_filter_get_filter($a
 }
 
 function privatemsg_filter_get_author_data($account) {
- static $author_data;
+  static $author_data;
 
   if (is_array($author_data)) {
     return $author_data;
@@ -209,22 +209,28 @@ function privatemsg_filter_get_author_da
   return $author_data;
 }
 
-function privatemsg_filter_get_tags_data($account) {
- static $tag_data;
+function privatemsg_filter_get_tags_data($uid = 0) {
+ static $tag_data = array();
  
-  if (is_array($tag_data)) {
-    return $tag_data;
+  if (isset($tag_data[$uid])) {
+    return $tag_data[$uid];
   }
 
   // Only show the tags that a user has used.
-  $sql = 'SELECT pmt.tag, pmt.tag_id FROM {pm_tags_index} pmti LEFT JOIN {pm_tags} pmt ON pmti.tag_id = pmt.tag_id WHERE pmti.uid = %d GROUP BY pmt.tag_id, pmt.tag';
-  $query = db_query($sql, $account->uid);
+  if ($uid) {
+    $sql = 'SELECT pmt.tag, pmt.tag_id FROM {pm_tags_index} pmti LEFT JOIN {pm_tags} pmt ON pmti.tag_id = pmt.tag_id WHERE pmti.uid = %d GROUP BY pmt.tag_id, pmt.tag';
+    $query = db_query($sql, $uid);
+  }
+  else {
+    $sql = 'SELECT tag, tag_id FROM {pm_tags} ORDER BY tag';
+    $query = db_query($sql);
+  }
 
   $tag_data = array();
   while ($result = db_fetch_object($query)) {
-    $tag_data[$result->tag_id] = $result->tag;
+    $tag_data[$uid][$result->tag_id] = $result->tag;
   }
-  return $tag_data;
+  return $tag_data[$uid];
 }
 
 function privatemsg_filter_dropdown(&$form_state, $account) {
@@ -236,6 +242,7 @@ function privatemsg_filter_dropdown(&$fo
       '#title' => t('Filter Messages'),
       '#collapsible' => TRUE,
       '#collapsed' => TRUE,
+      '#weight' => -10,
   );
   $form['filter']['search'] = array(
       '#type' => 'textfield',
@@ -253,7 +260,7 @@ function privatemsg_filter_dropdown(&$fo
   );
 
   // Only show form if the user has some messages tagged.
-  if (count($tag_data = privatemsg_filter_get_tags_data($account))) {
+  if (count($tag_data = privatemsg_filter_get_tags_data($account->uid))) {
     $form['filter']['tags'] = array(
       '#type' => 'select',
       '#title' => t('Tags'),
@@ -268,6 +275,7 @@ function privatemsg_filter_dropdown(&$fo
       '#value'    => t('Filter'),
       '#prefix'   => '<div id="privatemsg-filter-buttons">',
       '#weight'   => 10,
+      '#submit' => array('privatemsg_filter_dropdown_submit'),
     );
 
     $form['filter']['save'] = array(
@@ -275,6 +283,7 @@ function privatemsg_filter_dropdown(&$fo
       '#value'    => t('Save Filter'),
       '#suffix'   => '</div>',
       '#weight'   => 11,
+      '#submit' => array('privatemsg_filter_dropdown_submit'),
     );
 
     if ($filter = privatemsg_filter_get_filter($account)) {
@@ -302,7 +311,8 @@ function privatemsg_filter_dropdown_set_
       '#type'     => 'submit',
       '#value'    => t('Reset'),
       '#suffix'   => '</div>',
-      '#weight'   => 12
+      '#weight'   => 12,
+      '#submit' => array('privatemsg_filter_dropdown_submit'),
   );
   unset($form['filter']['save']['#suffix']);
 }
@@ -333,8 +343,7 @@ function privatemsg_filter_dropdown_subm
   $form_state['redirect'] = 'messages';
 }
 
-function privatemsg_filter_create_get_query($filter)
-{
+function privatemsg_filter_create_get_query($filter) {
   $query = array();
   if (isset($filter['tags']) && !empty($filter['tags'])) {
     $ids = array();
@@ -379,17 +388,6 @@ function privatemsg_filter_create_get_qu
 }
 
 /**
- * Implementation of hook_privatemsg_list_messages_alter().
- */
-function privatemsg_filter_privatemsg_list_messages_alter(&$content, $account) {
-  if (!empty($content['list']['content']) || privatemsg_filter_get_filter($account)) {
-    $content['tags']['content'] = drupal_get_form('privatemsg_filter_dropdown', $account);
-    $content['tags']['#weight'] = -5;
-  }
-}
-
-
-/**
  * Hook into the query builder to add the tagging info to the correct query
  */
 function privatemsg_filter_privatemsg_sql_list_alter(&$fragments) {
@@ -487,3 +485,42 @@ function privatemsg_filter_form_submit($
     drupal_set_message(t('Tagging information has been saved.'));
   }
 }
+
+function privatemsg_filter_form_alter(&$form, $form_state, $form_id) {
+  if ($form_id == 'privatemsg_list' && count($form['#rows']) > 0) {
+    $form += privatemsg_filter_dropdown($form_state, $form['#account']);
+  }
+}
+
+/**
+ * Implementation of hook_privatemsg_thread_operations().
+ */
+function privatemsg_filter_privatemsg_thread_operations() {
+  $operations = array();
+  $tags = privatemsg_filter_get_tags_data();
+
+  if (!empty($tags)) {
+    $operations['-'] = array(
+      'label' => t('Apply Tag...'),
+    );
+    foreach ($tags as $tag_id => $tag) {
+      $operations[$tag_id] = array(
+        'label' => '- '. $tag,
+        'callback' => '_privatemsg_filter_thread_tag',
+        'callback arguments' => array('tag_id' => $tag_id),
+      );
+    }
+  }
+  return $operations;
+}
+
+function _privatemsg_filter_thread_tag($threads, $tag_id) {
+  global $user;
+
+  foreach ($threads as $thread) {
+    if (db_result(db_query('SELECT COUNT(*) FROM {pm_tags_index} WHERE tag_id = %d AND (uid = %d AND thread_id = %d)', $tag_id, $user->uid, $thread)) == 0) {
+      db_query('INSERT INTO {pm_tags_index} (tag_id, uid, thread_id) VALUES (%d, %d, %d)', $tag_id, $user->uid, $thread);
+    }
+  }
+}
+
