Index: privatemsg.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.module,v
retrieving revision 1.70.2.30.2.91.2.58
diff -u -p -r1.70.2.30.2.91.2.58 privatemsg.module
--- privatemsg.module	10 Jul 2009 16:53:18 -0000	1.70.2.30.2.91.2.58
+++ privatemsg.module	14 Jul 2009 21:27:32 -0000
@@ -1826,37 +1826,45 @@ function privatemsg_list_submit($form, &
     $operation = $operations[$keys[1]];
   }
 
+  // Only execute something if we have a valid callback and atleast one checked thread.
+  if (!empty($operation['callback'])) {
+    privatemsg_operation_execute($operation, $form_state['values']['threads']);
+  }
+}
+
+function privatemsg_operation_execute($operation, $threads) {
   // Filter out unchecked threads, this gives us an array of "checked" threads.
-  $threads = array_filter($form_state['values']['threads']);
+  $threads = array_filter($threads);
 
-  // Only execute something if we have a valid callback and atleast one checked thread.
-  if (!empty($operation['callback']) && !empty($threads)) {
-    // Add in callback arguments if present.
-    if (isset($operation['callback arguments'])) {
-      $args = array_merge(array($threads), $operation['callback arguments']);
+  if (empty($threads)) {
+    // Do not execute anything if there are no checked threads.
+    return;
+  }
+  // Add in callback arguments if present.
+  if (isset($operation['callback arguments'])) {
+    $args = array_merge(array($threads), $operation['callback arguments']);
+  }
+  else {
+    $args = array($threads);
+  }
+  // Execute the chosen action and pass the defined arguments.
+  call_user_func_array($operation['callback'], $args);
+
+  // Check if that operation has defined a undo callback
+  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 {
-      $args = array($threads);
-    }
-    // Execute the chosen action and pass the defined arguments.
-    call_user_func_array($operation['callback'], $args);
-
-    // Check if that operation has defined a undo callback
-    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);
-      }
-      // Store the undo callback in the session and display a "Undo" link.
-      // @todo: Provide a more flexible solution for such an undo action, operation defined string for example.
-      $_SESSION['privatemsg']['undo callback'] = array('function' => $undo_function, 'args' => $undo_args);
-      $undo = l(t('undone'), 'messages/undo/action', array('query' => drupal_get_destination()));
-
-      drupal_set_message(t('The previous action can be !undo.', array('!undo' => $undo)));
+      $undo_args = array($threads);
     }
+    // Store the undo callback in the session and display a "Undo" link.
+    // @todo: Provide a more flexible solution for such an undo action, operation defined string for example.
+    $_SESSION['privatemsg']['undo callback'] = array('function' => $undo_function, 'args' => $undo_args);
+    $undo = l(t('undone'), 'messages/undo/action', array('query' => drupal_get_destination()));
+    
+    drupal_set_message(t('The previous action can be !undo.', array('!undo' => $undo)));
   }
 }
 
Index: privatemsg_filter/privatemsg_filter.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg_filter/privatemsg_filter.module,v
retrieving revision 1.1.2.17
diff -u -p -r1.1.2.17 privatemsg_filter.module
--- privatemsg_filter/privatemsg_filter.module	11 Jul 2009 13:37:31 -0000	1.1.2.17
+++ privatemsg_filter/privatemsg_filter.module	14 Jul 2009 21:27:33 -0000
@@ -308,11 +308,45 @@ function privatemsg_filter_create_get_qu
  * Implementation of hook_form_alter() to add a filter widget to the message listing pages.
  */
 function privatemsg_filter_form_privatemsg_list_alter(&$form, $form_state) {
+  global $user;
+  
   if (privatemsg_user_access('filter private messages')) {
     $form += privatemsg_filter_dropdown($form_state, $form['#account']);
   }
+
+  $tags = privatemsg_filter_get_tags_data($user);
+  if (privatemsg_user_access('filter private messages') && !empty($tags)) {
+    $options = array();
+    $options[] = t('Apply Tag...');
+    foreach ($tags as $tag_id => $tag) {
+      $options[$tag_id] = $tag;
+    }
+    $form['actions']['tag-add'] = array(
+      '#type'          => 'select',
+      '#options'       => $options,
+      '#default_value' => 0,
+      // Execute the submit button if a operation has been selected.
+      '#attributes'    => array('onchange' => "$('#edit-tag-add-submit').click()"),
+    );
+    $form['actions']['tag-add-submit'] = array(
+        '#type'       => 'submit',
+        '#value'      => t('Apply Tag'),
+        '#submit'     => array('privatemsg_filter_add_tag_submit'),
+        '#attributes' => array('class' => 'privatemsg-action-button'),
+    );
+  }
 }
 
+/**
+ * Form callback for adding a tag to threads.
+ */
+function privatemsg_filter_add_tag_submit($form, &$form_state) {
+  $operation = array(
+    'callback' => 'privatemsg_filter_thread_tag',
+    'callback arguments' => array('tag_id' => $form_state['values']['tag-add']),
+  );
+  privatemsg_operation_execute($operation, $form_state['values']['threads']);
+}
 
 /**
  * Hook into the query builder to add the tagging info to the correct query
@@ -556,4 +590,27 @@ function privatemsg_filter_sql_tags_auto
     $fragments['query_args']['where'] += $tags;
   }
   $fragments['order_by'][] = 'pmt.tag ASC';
+}
+/**
+ * Tag one or multiple threads with a tag.
+ *
+ * @param $threads A single thread id or an array of thread ids.
+ * @param $tag_id Id of the tag.
+ */
+function privatemsg_filter_thread_tag($threads, $tag_id, $account = NULL) {
+  if (!is_array($threads)) {
+    $threads = array($threads);
+  }
+  if (empty($account)) {
+    global $user;
+    $account = drupal_clone($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) {
+      drupal_set_message(sprintf('INSERT INTO {pm_tags_index} (tag_id, uid, thread_id) VALUES (%d, %d, %d)', $tag_id, $user->uid, $thread));
+      db_query('INSERT INTO {pm_tags_index} (tag_id, uid, thread_id) VALUES (%d, %d, %d)', $tag_id, $user->uid, $thread);
+    }
+  }
+  drupal_set_message(t('Tagged %count threads.', array('%count' => count($threads))));
 }
\ No newline at end of file
