? .cvsignore
? privatemsg.list_messages.patch
? privatemsg.list_messages2.patch
? translations
Index: privatemsg.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.module,v
retrieving revision 1.70.2.30.2.91.2.13
diff -u -p -r1.70.2.30.2.91.2.13 privatemsg.module
--- privatemsg.module	27 Jan 2009 16:55:18 -0000	1.70.2.30.2.91.2.13
+++ privatemsg.module	8 Feb 2009 03:20:00 -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'),
     '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'),
     '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'),
     'access arguments' => array('read privatemsg'),
     'type'             => MENU_LOCAL_TASK,
     'weight'           => -5,
@@ -240,8 +235,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'),
@@ -296,25 +291,8 @@ 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);
-
-  uasort($content, 'element_sort');
-
-  $expand = '';
-  foreach ($content as $element) {
-    $expand .= $element['content'];
-  }
-  return $expand;
-}
-
-/**
- * Display the list of messages for a given user, called by privatemsg_privatemsg_alter
- */
-function privatemsg_list_messages($account) {
   switch (arg(1)) {
     case 'sent':
       $query = _privatemsg_assemble_query('privatemsg_list_sent', $account);
@@ -325,68 +303,177 @@ function privatemsg_list_messages($accou
   }
   $result = pager_query($query['query'], variable_get('privatemsg_per_page', 25), 0, $query['count']);
 
-  $rows = array();
+  $threads = array();
+  $form['#rows'] = array();
   while ($thread = db_fetch_array($result)) {
-    $rows[] = theme('privatemsg_message_row', $thread);
+    $form['#rows'][] = $thread;
+    $threads[$thread['thread_id']] = '';
   }
 
-  $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');
-      }
+  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'),
+    );
+  }
+
+  $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;
+}
+
+/**
+ * Implementation of hook_privatemsg_thread_operations().
+ */
+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),
+    ),
+    'mark as unread' => array(
+      'label' => t('Mark as unread'),
+      'callback' => '_privatemsg_thread_setnew',
+      'callback arguments' => array('new' => 1),
+    ),
+  );
+  return $operations;
+}
 
-    $content = theme('table', $head, $rows);
-    $content .= theme('pager');
-    drupal_add_css(drupal_get_path('module', 'privatemsg') .'/styles/privatemsg-list.css');
+function _privatemsg_thread_setnew($threads, $new_status = 0) {
+  global $user;
+
+  foreach ($threads as $thread) {
+    $query = "UPDATE {pm_index} SET is_new = %d WHERE thread_id = %d AND uid = %d";
+    db_query($query, $new_status, $thread, $user->uid);
+  }
+}
+
+/**
+ * Process privatemsg_list form submissions.
+ *
+ * Execute the chosen 'Update option' on the selected nodes. 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 ($function = $operation['callback']) {
+    // Add in callback arguments if present.
+    if (isset($operation['callback arguments'])) {
+      $args = array_merge(array($threads), $operation['callback arguments']);
+    }
+    else {
+      $args = array($threads);
+    }
+    call_user_func_array($function, $args);
   }
   else {
-    drupal_set_message(t('No messages to display.'));
+    // We need to rebuild the form to go to a second step.  For example, to
+    // show the confirmation form for the deletion of nodes.
+    $form_state['rebuild'] = TRUE;
   }
-
-  return $content;
 }
 
-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));
+function privatemsg_list_submit_delete($form, &$form_state) {
+  global $user;
 
-  return l($text, 'messages', array('attributes' => array('id' => 'privatemsg-new-link')));
+  // Filter out unchecked nodes
+  $threads = array_filter($form_state['values']['threads']);
+  foreach ($threads as $thread) {
+    privatemsg_thread_delete($thread);
+  }
+}
+
+function privatemsg_thread_delete($thread_id) {
+  db_query('UPDATE {pm_index} SET deleted = 1 WHERE thread_id = %d AND uid = %d', $thread, $user->uid);
 }
 
 /**
- * Theme a row of messages
- *
+ * Display the list of messages for a given user, called by privatemsg_privatemsg_alter
  */
-function theme_privatemsg_message_row($row) {
-  $themed_row = array();
+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;
+  }
+  unset($form['rows']);
 
-  $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>';
-    }
-  }
-//  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>';
+  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')));
 }
 
 /**
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	8 Feb 2009 03:20:01 -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;
     }
@@ -86,6 +86,16 @@ function pm_block_user_form_submit($form
   $form_state['redirect'] = $form_state['values']['destination'];
 }
 
+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;
+}
+
 /**
  * Implementation of hook_privatemsg_block_message.
  */
@@ -116,4 +126,45 @@ 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_setblock',
+      'callback arguments' => array('block' => 1),
+    ),
+    'unblock' => array(
+      'label' => t('Unblock all authors'),
+      'callback' => '_pm_block_user_thread_setblock',
+      'callback arguments' => array('block' => 0),
+    ),
+  );
+  return $operations;
+}
+
+function _pm_block_user_thread_setblock($threads, $block = 1) {
+  global $user;
+
+  $query = _privatemsg_assemble_query('pm_block_user_threadauthors', $threads);
+  $result = db_query($query['query']);
+  while ($author = db_fetch_array($result)) {
+    if ($author['uid'] != $user->uid) {
+      pm_block_user_blockauthor($author['uid'], $user->uid, $block);
+    }
+  }
+}
+
+function pm_block_user_pm_block_user_threadauthors_alter(&$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';
 }
\ 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.2
diff -u -p -r1.1.2.2 privatemsg_filter.module
--- privatemsg_filter/privatemsg_filter.module	23 Dec 2008 16:46:13 -0000	1.1.2.2
+++ privatemsg_filter/privatemsg_filter.module	8 Feb 2009 03:20:01 -0000
@@ -1,5 +1,6 @@
 <?php
-// $Id: privatemsg_filter.module,v 1.1.2.2 2008/12/23 16:46:13 litwol Exp $
+// $Id: privatemsg_filter.module,v 1.1.2.1 2008/12/19 22:05:19 litwol Exp $
+
 
 /**
  * @file
@@ -156,7 +157,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;
@@ -199,7 +200,7 @@ function privatemsg_filter_get_author_da
   if (is_array($author_data)) {
     return $author_data;
   }
-  $sql = 'SELECT DISTINCT pm.author, u.name FROM {pm_message} pm INNER JOIN {pm_index pmi} ON pm.mid = pmi.mid INNER JOIN {users} u ON pm.author = u.uid WHERE pmi.uid = %d AND u.uid > 0 ORDER BY u.name';
+  $sql = 'SELECT DISTINCT pm.author, u.name FROM {pm_message} pm INNER JOIN {pm_index pmi} ON pm.mid = pmi.mid INNER JOIN {users} u ON pm.author = u.uid WHERE pmi.uid = %d AND u.uid > 0 AND pmi.deleted = 0 ORDER BY u.name';
   $query = db_query($sql, $account->uid);
 
   $author_data = array();
@@ -209,22 +210,27 @@ 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']);
 }
@@ -378,16 +388,6 @@ function privatemsg_filter_create_get_qu
   return $query;
 }
 
-/**
- * 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
@@ -423,6 +423,44 @@ function privatemsg_filter_privatemsg_li
   }
 }
 
+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);
+    }
+  }
+}
+
 function privatemsg_filter_privatemsg_view_messages_alter(&$content, $count) {
   if ($count > 0) {
     $content['tags']['content'] = drupal_get_form('privatemsg_filter_form');
