Index: privatemsg.api.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/privatemsg/privatemsg.api.php,v
retrieving revision 1.1.2.5.2.8
diff -u -p -r1.1.2.5.2.8 privatemsg.api.php
--- privatemsg.api.php	31 Oct 2010 13:11:22 -0000	1.1.2.5.2.8
+++ privatemsg.api.php	23 Nov 2010 00:54:32 -0000
@@ -416,16 +416,13 @@ function hook_privatemsg_block_message($
 /**
  * Add content to the view thread page.
  *
- * Each element in content contains a 'value' and a '#weight' key, the weight
- * is used set the order of the different parts.
- *
  * @param $content
- *   Content array
- * @param $message_count
- *   Amount of messages
+ *   Render-able array, contains the thread object in #thread.
  */
-function hook_privatemsg_view_messages($content, $message_count) {
-
+function hook_privatemsg_view_alter($content) {
+  if (privatemsg_user_access('tag private messages')) {
+    $content['tags'] = privatemsg_filter_show_tags($content['#thread']['thread_id'], !empty($_GET['show_tags_form']));
+  }
 }
 
 /**
Index: privatemsg.pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/privatemsg/privatemsg.pages.inc,v
retrieving revision 1.1.2.32
diff -u -p -r1.1.2.32 privatemsg.pages.inc
--- privatemsg.pages.inc	21 Oct 2010 09:12:37 -0000	1.1.2.32
+++ privatemsg.pages.inc	23 Nov 2010 00:54:42 -0000
@@ -690,6 +690,10 @@ function privatemsg_autocomplete($string
 function  privatemsg_view($thread) {
   drupal_set_title($thread['subject']);
 
+  $content = array(
+    '#thread' => $thread,
+  );
+
   if ($thread['to'] != $thread['message_count'] || !empty($thread['start'])) {
     // Generate paging links.
     $older = '';
@@ -748,6 +752,8 @@ function  privatemsg_view($thread) {
     drupal_set_message(t('This conversation is being viewed with escalated privileges and may not be the same as shown to normal users.'), 'warning');
   }
 
+  drupal_alter('privatemsg_view', $content);
+
   return $content;
 }
 
Index: privatemsg_filter/privatemsg_filter.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/privatemsg/privatemsg_filter/privatemsg_filter.css,v
retrieving revision 1.1
diff -u -p -r1.1 privatemsg_filter.css
--- privatemsg_filter/privatemsg_filter.css	12 Dec 2008 21:28:04 -0000	1.1
+++ privatemsg_filter/privatemsg_filter.css	23 Nov 2010 00:54:43 -0000
@@ -6,4 +6,26 @@ form#privatemsg-filter-dropdown #edit-au
 
 form#privatemsg-filter-dropdown #privatemsg-filter-buttons {
   clear: left;
+}
+
+form#privatemsg-filter-form div.form-item {
+    float: left;
+    margin-right: 5px;
+}
+
+form#privatemsg-filter-form {
+    clear: both;
+}
+
+div#privatemsg-filter-tags a, span.privatemsg-filter-tags-label {
+    padding-right: 10px;
+}
+
+div#privatemsg-filter-tags {
+    margin-top: -15px;
+    margin-bottom: 10px;
+}
+
+div.privatemsg-message-participants {
+    clear:both;
 }
\ No newline at end of file
Index: privatemsg_filter/privatemsg_filter.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/privatemsg/privatemsg_filter/privatemsg_filter.module,v
retrieving revision 1.1.2.17.2.30
diff -u -p -r1.1.2.17.2.30 privatemsg_filter.module
--- privatemsg_filter/privatemsg_filter.module	6 Oct 2010 19:46:35 -0000	1.1.2.17.2.30
+++ privatemsg_filter/privatemsg_filter.module	23 Nov 2010 00:54:46 -0000
@@ -252,9 +252,6 @@ function privatemsg_filter_remove_tags($
   if (!is_array($threads)) {
     $threads = array($threads);
   }
-  if (!is_array($tag_ids)) {
-    $tag_ids = array($tag_ids);
-  }
   if (empty($account)) {
     global $user;
     $account = $user;
@@ -269,6 +266,10 @@ function privatemsg_filter_remove_tags($
       ->execute();
   }
   else {
+    if (!is_array($tag_ids)) {
+      $tag_ids = array($tag_ids);
+    }
+
     //Delete tag mapping for the specified tag.
     db_delete('pm_tags_index')
       ->condition('uid', $account->uid)
@@ -731,77 +732,127 @@ function privatemsg_filter_query_private
 }
 
 /**
- * Hook into the view messages page to add a form for tagging purposes.
+ * Implements hook_privatemsg_view_alter().
  */
-function privatemsg_filter_page_alter(&$page) {
-  if (isset($page['content']['system_main']['messages']) && count($page['content']['system_main']['messages']) && isset($page['content']['system_main']['participants']) && privatemsg_user_access('tag private messages')) {
-    $page['content']['system_main']['tags'] = drupal_get_form('privatemsg_filter_form');
-    $page['content']['system_main']['tags']['#weight'] = variable_get('privatemsg_filter_tagfield_weight', 10);
+function privatemsg_filter_privatemsg_view_alter(&$content) {
+  if (privatemsg_user_access('tag private messages')) {
+    $content['tags'] = privatemsg_filter_show_tags($content['#thread']['thread_id'], !empty($_GET['show_tags_form']));
+  }
+}
+
+function privatemsg_filter_show_tags($thread_id, $show_form) {
+  global $user;
+
+  drupal_add_css(drupal_get_path('module', 'privatemsg_filter') . '/privatemsg_filter.css');
+
+  $element = array(
+    '#prefix' => '<div id="privatemsg-filter-tags">',
+    '#suffix' => '</div>',
+    '#weight' => -3,
+  );
+
+  if (!$show_form) {
+    $element['text'] = array(
+      '#prefix' => '<span class="privatemsg-filter-tags-label">',
+      '#suffix' => '</span>',
+    );
+
+    $query = _privatemsg_assemble_query(array('tags', 'privatemsg_filter'), $user, array($thread_id));
+    if ($query->countQuery()->execute()->fetchField() == 0) {
+      $element['text']['#markup'] = t('No tags');
+
+    }
+    else {
+      $element['text']['#markup'] = t('Tags:');
+
+      foreach ($query->execute()->fetchCol(1) as $tag) {
+        $element['tags'][] = array(
+          '#type' => 'link',
+          '#title' => $tag,
+          '#href' => 'messages',
+          '#options' => array(
+            'attributes' => array('title' => $tag),
+            'query' => array('tags' => $tag),
+          ),
+        );
+      }
+    }
+
+    $element['link'] = array(
+      '#type' => 'link',
+      '#href' => $_GET['q'],
+      '#options' => array('query' => array('show_tags_form' => TRUE)),
+      '#title' => t('(modify tags)'),
+    );
+    return $element;
+  }
+  else {
+    return drupal_get_form('privatemsg_filter_form', $thread_id) + $element;
   }
 }
 
 /**
  * Form to show and allow modification of tagging information for a conversation.
  */
-function privatemsg_filter_form($form, &$form_state) {
+function privatemsg_filter_form($form, &$form_state, $thread_id) {
   global $user;
-  $thread_id = arg(2);
 
   // Get a list of current tags for this thread
   $query = _privatemsg_assemble_query(array('tags', 'privatemsg_filter'), $user, array($thread_id));
   $count = $query->countQuery()->execute()->fetchField();
   $tags = implode(', ', $query->execute()->fetchCol(1));
 
-  $form['tags'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Tags'),
-    '#access' => privatemsg_user_access('tag private messages'),
-    '#collapsible' => TRUE,
-    '#collapsed' => empty($count) ? TRUE : FALSE,
-  );
-  $form['tags']['user_id'] = array(
+  $form['user_id'] = array(
     '#type' => 'value',
     '#value' => $user->uid,
   );
-  $form['tags']['thread_id'] = array(
+  $form['thread_id'] = array(
     '#type' => 'value',
     '#value' => $thread_id,
   );
 
-  $form['tags']['tags'] = array(
+  $form['tags'] = array(
     '#type'               => 'textfield',
     '#title'              => t('Tags for this conversation'),
-    '#description'        => t('Separate multiple tags with commas.'),
-    '#size'               => 50,
+    '#title_display'      => 'invisible',
+    '#size'               => 30,
     '#default_value'      => $tags,
     '#autocomplete_path'  => 'messages/filter/tag-autocomplete',
   );
 
-  $form['tags']['submit'] = array(
+  $form['modify_tags'] = array(
     '#type'     => 'submit',
     '#value'    => t('Tag this conversation'),
-    '#submit'   => array('privatemsg_filter_form_submit'),
+  );
+
+  $form['cancel'] = array(
+    '#type' => 'link',
+    '#href' => $_GET['q'],
+    '#title' => t('Cancel'),
+    '#attributes' => array('id' => 'privatemsg-filter-tags-cancel'),
+    '#weight' => 50,
   );
 
   return $form;
 }
 
+/**
+ * Form builder function, display a form to modify tags on a thread.
+ */
 function privatemsg_filter_form_submit($form, &$form_state) {
-  if (isset($form_state['values']['submit'])) {
-    $tags = explode(',', $form_state['values']['tags']);
+  $tags = explode(',', $form_state['values']['tags']);
 
-    // Step 1 - Delete all tag mapping. I cannot think of a better way to remove tags that are no longer in the textfield, so ideas welcome.
-    privatemsg_filter_remove_tags($form_state['values']['thread_id']);
+  // Step 1 - Delete all tag mapping.
+  privatemsg_filter_remove_tags($form_state['values']['thread_id']);
 
-    // Step 2 - Get the id for each of the tags.
-    $tag_ids = privatemsg_filter_create_tags($tags);
+  // Step 2 - Get the id for each of the tags.
+  $tag_ids = privatemsg_filter_create_tags($tags);
 
-    // Step 3 - Save all the tagging data.
-    foreach ($tag_ids as $tag_id) {
-      privatemsg_filter_add_tags($form_state['values']['thread_id'], $tag_id);
-    }
-    drupal_set_message(t('Tagging information has been saved.'));
+  // Step 3 - Save all the tagging data.
+  foreach ($tag_ids as $tag_id) {
+    privatemsg_filter_add_tags($form_state['values']['thread_id'], $tag_id);
   }
+  drupal_set_message(t('Tagging information has been saved.'));
 }
 
 /**
Index: privatemsg_filter/privatemsg_filter.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/privatemsg/privatemsg_filter/privatemsg_filter.test,v
retrieving revision 1.1.4.15
diff -u -p -r1.1.4.15 privatemsg_filter.test
--- privatemsg_filter/privatemsg_filter.test	6 Oct 2010 19:46:35 -0000	1.1.4.15
+++ privatemsg_filter/privatemsg_filter.test	23 Nov 2010 00:54:48 -0000
@@ -133,32 +133,51 @@ class PrivatemsgTagsTestCase extends Dru
     parent::setUp('privatemsg', 'privatemsg_filter');
   }
 
-  function testCreateUserTag() {
+  /**
+   * Create and update tags on a single thread.
+   */
+  function testFilterFormSingleThread() {
     $webuser = $this->drupalCreateuser(array('read privatemsg', 'write privatemsg', 'tag private messages', 'create private message tags'));
 
     // Create a new thread through the api.
     $response = privatemsg_new_thread(array($webuser), $this->randomName(10), $this->randomName(20), array('author' => $webuser));
     $thread_id = $response['message']->thread_id;
 
+    $tags = array($this->randomName(), $this->randomName(), $this->randomName(), $this->randomName());
     $edit = array(
-      'tags' => 'Awesome bananas, Banana',
+      'tags' => $tags[0] . ', ' . $tags[1],
     );
     $this->drupalLogin($webuser);
-    $this->drupalPost('messages/view/' . $thread_id, $edit, t('Tag this conversation'));
-    $this->assertRaw('Awesome bananas,', t('Found message tag'));
-    $this->assertRaw('Banana', t('Found message tag'));
-
+    $this->drupalGet('messages/view/' . $thread_id);
+    $this->clickLink(t('(modify tags)'));
+    $this->drupalPost(NULL, $edit, t('Tag this conversation'));
+    $this->assertText($tags[0], t('Found message tag'));
+    $this->assertText($tags[1], t('Found message tag'));
 
     // Create a another thread through the api.
     $response = privatemsg_new_thread(array($webuser), $this->randomName(10), $this->randomName(20), array('author' => $webuser));
     $thread_id = $response['message']->thread_id;
 
     $edit = array(
-      'tags' => 'Banana, Apple',
+      'tags' => $tags[1] . ', ' . $tags[2],
     );
-    $this->drupalPost('messages/view/' . $thread_id, $edit, t('Tag this conversation'));
-    $this->assertRaw('Banana', t('Found message tag'));
-    $this->assertRaw('Apple,', t('Found message tag'));
+    $this->drupalGet('messages/view/' . $thread_id);
+    $this->clickLink(t('(modify tags)'));
+    $this->drupalPost(NULL, $edit, t('Tag this conversation'));
+    $this->assertText($tags[1], t('Found message tag'));
+    $this->assertText($tags[2], t('Found message tag'));
+
+    // Change tags.
+    $edit = array(
+      'tags' => $tags[0],
+    );
+    $this->drupalGet('messages/view/' . $thread_id);
+    $this->clickLink(t('(modify tags)'));
+    $this->drupalPost(NULL, $edit, t('Tag this conversation'));
+    $this->assertText($tags[0], t('Found message tag'));
+    $this->assertNoText($tags[1], t('Tag has been removed.'));
+    $this->assertNoText($tags[2], t('Tag has been removed.'));
+
   }
 
   function testTagsAdministration() {
@@ -334,13 +353,17 @@ class PrivatemsgTagsTestCase extends Dru
     $response = privatemsg_new_thread(array($webuser), $subject1 = $this->randomName(10), $this->randomName(20), array('author' => $webuser));
     $thread_id = $response['message']->thread_id;
 
+    $tag1 = $this->randomName();
+    $tag2 = $this->randomName();
     $edit = array(
-      'tags' => 'Awesome bananas, Banana',
+      'tags' => $tag1 . ', ' . $tag2,
     );
     $this->drupalLogin($webuser);
-    $this->drupalPost('messages/view/' . $thread_id, $edit, t('Tag this conversation'));
-    $this->assertRaw('Awesome bananas,', t('Found message tag'));
-    $this->assertRaw('Banana', t('Found message tag'));
+    $this->drupalGet('messages/view/' . $thread_id);
+    $this->clickLink(t('(modify tags)'));
+    $this->drupalPost(NULL, $edit, t('Tag this conversation'));
+    $this->assertText($tag1, t('Found message tag'));
+    $this->assertText($tag2, t('Found message tag'));
 
     // Create another thread.
     $response = privatemsg_new_thread(array($webuser), $subject2 = $this->randomName(10), $this->randomName(20), array('author' => $webuser));
@@ -350,31 +373,33 @@ class PrivatemsgTagsTestCase extends Dru
     $rows = $this->xpath('//tbody/tr');
     foreach ($rows as $row) {
       if ($row->td[2]->a == $subject1) {
-        // The first thread should have both tags.
-        $this->assertEqual('Awesome bananas', $row->td[1]->a[0], t('First thread is tagged with Awesome bananas'));
-        $this->assertEqual('Banana', $row->td[1]->a[1], t('First thread is tagged with Banana'));
+        // The first thread should have both tags. Try both ways as the order
+        // might change.
+        $verify = (($tag1 == $row->td[1]->a[0] && $tag2 == $row->td[1]->a[1]) || ($tag1 == $row->td[1]->a[1] && $tag2 == $row->td[1]->a[0]));
+        $this->assertTrue($verify, t('First thread is correctly tagged.'));
       }
       if ($row->td[2]->a == $subject2) {
         // The second thread should have no tags.
-        $this->assertEqual('', $row->td[1], t('Second thread is not tagged'));
+        $this->assertEqual('', $row->td[1], t('Second thread is not tagged.'));
       }
     }
 
     $add_tag = array(
       'list[' . $thread_id2 . ']' => 1,
-      'tag-add' => 'Banana',
+      'tag-add' => $tag2,
     );
     $this->drupalPost(NULL, $add_tag, t('Apply Tag'));
     $rows = $this->xpath('//tbody/tr');
     foreach ($rows as $row) {
       if ($row->td[2]->a == $subject1) {
-        // The first thread should have both tags.
-        $this->assertEqual('Awesome bananas', $row->td[1]->a[0], t('First thread is tagged with Awesome bananas'));
-        $this->assertEqual('Banana', $row->td[1]->a[1], t('First thread is tagged with Banana'));
+        // The first thread should have both tags. Try both ways as the order
+        // might change.
+        $verify = (($tag1 == $row->td[1]->a[0] && $tag2 == $row->td[1]->a[1]) || ($tag1 == $row->td[1]->a[1] && $tag2 == $row->td[1]->a[0]));
+        $this->assertTrue($verify, t('First thread is correctly tagged.'));
       }
       if ($row->td[2]->a == $subject2) {
         // The second thread should have one tag.
-        $this->assertEqual('Banana', $row->td[1]->a, t('Second thread is correctly tagged'));
+        $this->assertEqual($tag2, $row->td[1]->a, t('Second thread is correctly tagged.'));
       }
     }
 
@@ -388,14 +413,14 @@ class PrivatemsgTagsTestCase extends Dru
     foreach ($rows as $row) {
       if ($row->td[2]->a == $subject1) {
         // The first thread should have only one tag now.
-        $this->assertEqual('Awesome bananas', $row->td[1]->a, t('First thread is tagged with Awesome bananas'));
+        $this->assertEqual($tag1, $row->td[1]->a, t('First thread is correctly tagged.'));
       }
       if ($row->td[2]->a == $subject2) {
         // The second thread should have no tags.
-        $this->assertEqual('', $row->td[1], t('Second thread is not tagged'));
+        $this->assertEqual('', $row->td[1], t('Second thread is not tagged.'));
       }
     }
-    $this->assertNoText('Banana', t('Banana tag is not displayed anymore'));
+    $this->assertNoText($tag2, t('Second tag is not displayed anymore.'));
   }
 }
 
