Index: privatemsg.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.module,v
retrieving revision 1.70.2.30.2.91.2.11
diff -u -p -r1.70.2.30.2.91.2.11 privatemsg.module
--- privatemsg.module	26 Jan 2009 19:53:26 -0000	1.70.2.30.2.91.2.11
+++ privatemsg.module	26 Jan 2009 20:35:10 -0000
@@ -261,9 +261,12 @@ function privatemsg_preprocess_privatems
    */
   $vars['message_timestamp'] = format_date($message['timestamp'], 'small');
   $vars['message_body'] = check_markup($message['body']);
-  if ( isset($message['mid']) ) { //safe for message preview
-    $vars ['message_actions'] = privatemsg_per_message_actions($message['mid']);
+  if (isset($vars['mid'])) {
+    $vars['message_actions'][] = l(t('Delete message'), 'messages/delete/'. $vars['mid']);
   }
+
+  // call hook_privatemsg_message_view_alter
+  drupal_alter('privatemsg_message_view', $vars);
 }
 
 function privatemsg_preprocess_privatemsg_to(&$vars) {
@@ -554,6 +557,7 @@ function privatemsg_new(&$form_state, $a
     '#description'        => t('Separate multiple names with commas.'),
     '#default_value'      => $recipient,//populate this later
     '#required'           => TRUE,
+    '#weight'             => -10,
     '#size'               => 50,
     '#autocomplete_path'  => 'messages/user-name-autocomplete',
     // Do not hardcode #maxlength, make it configurable by number of recipients, not their name length.
@@ -564,6 +568,7 @@ function privatemsg_new(&$form_state, $a
     '#size'               => 50,
     '#maxlength'          => 255,
     '#default_value'      => $subject,
+    '#weight'             => -5,
     '#required'           => TRUE,
   );
   $form['privatemsg']['body']       = array(
@@ -571,17 +576,20 @@ function privatemsg_new(&$form_state, $a
     '#title'              => t('Message'),
     '#cols'               => 10,
     '#rows'               => 6,
+    '#weight'             => 0,
     '#default_value'      => $body,
   );
   $form['privatemsg']['preview'] = array(
     '#type'               => 'submit',
     '#value'              => t('Preview message'),
     '#submit'             => array('pm_preview'),
+    '#weight'             => 10,
   );
   $form['privatemsg']['submit'] = array(
     '#type'               => 'submit',
     '#value'              => t('Send message'),
     '#submit'             => array('pm_send'),
+    '#weight'             => 15,
   );
   $url = 'messages';
   if (isset($_REQUEST['destination'])) {
@@ -590,9 +598,17 @@ function privatemsg_new(&$form_state, $a
 
   $form['privatemsg']['cancel'] = array(
     '#value'              => l(t('Cancel'), $url, array('attributes' => array('id' => 'edit-cancel'))),
+    '#weight'             => 20,
   );
   $form['#validate'][]    = 'pm_send_validate';
 
+  //modules can store data here, everything stored here will be available
+  //in $message, similar to #node
+  $form['#privatemsg_message'] = array();
+  if (!empty($form_state['#privatemsg_message'])) {
+     $form['#privatemsg_message'] = $form_state['#privatemsg_message'];
+  }
+
   return $form;
 }
 
@@ -600,6 +616,7 @@ function pm_send_validate($form, &$form_
 //  drupal_set_message('<pre>'. print_r($form_state['values'], 1) . '</pre>');
   // The actual message that is being sent, we create this during validation and pass to submit to send out.
   $message = array();
+  $message += $form['#privatemsg_message'];
   $message['body']      = $form_state['values']['body'];
   $message['subject']   = $form_state['values']['subject'];
   $message['author']    = $form_state['values']['author'];
@@ -608,10 +625,7 @@ function pm_send_validate($form, &$form_
     $message['thread_id'] = $form_state['values']['thread_id'];
   }
 
-  // Some editors add tags to the body which will fool the formapi into thinking there is content.
-  if (trim(strip_tags($form_state['values']['body'])) == '' && $form['privatemsg']['body']['#required'] == TRUE) {
-    form_set_error('body', t('Blank messages are not allowed.'));
-  }
+  $form_state['#privatemsg_message'] = $form['#privatemsg_message'];
 
   // Verify that recipient's name syntax is correct.
   $fragments = explode(',', $form_state['values']['recipient']);
@@ -633,17 +647,7 @@ function pm_send_validate($form, &$form_
   // Verify users exist and load their accounts.
   foreach ($valid as $index => $name) {
     if ($recipient = user_load(array('name' => $name))) {
-      // Check for blocked users. Modules blocking the message will return a message if they block, or nothing if they don't.
-      $results = module_invoke_all('privatemsg_block_message', $message['author'], $recipient);
-      if (count($results)) {
-        $invalid[$name] = $name;
-        foreach ($results as $result) {
-          drupal_set_message($result);
-        }
-      }
-      else {
-        $message['recipients'][$recipient->uid] = $recipient;
-      }
+      $message['recipients'][$recipient->uid] = $recipient;
     }
     else {
       // Here we add more invalid names due to the fact that they don't exist.
@@ -664,46 +668,24 @@ function pm_send_validate($form, &$form_
    * 2) Names that remain will be put into a recipients array.
    */
 
+  $errors = _privatemsg_validate_message($message, $message['author'], TRUE);
+  if (!empty($errors)) {
+      foreach ($errors as $error) {
+        form_set_error('body', $error);
+      }
+  }
+
   $form_state['validate_built_message'] = $message;
   if (!empty($invalid)) {
     drupal_set_message(t('The following users will not receive this private message: @invalid', array('@invalid' => implode(", ", $invalid))), 'error');
   }
-  if (empty($message['recipients'])) {
-    form_set_error('error', t('There are no valid recipients.'));
-  }
   $form_state['values']['recipient'] = implode(', ', array_diff($valid, $invalid));
 }
 
 function pm_send($form, &$form_state) {
-  $message = $form_state['validate_built_message'];
-
-  // 1) Save the message body first.
-  $args = array();
-  $args[] = $message['subject'];
-  $args[] = $message['author']->uid;
-  $args[] = $message['body'];
-  $args[] = $message['timestamp'];
-  $query = "INSERT INTO {pm_message} (subject, author, body, timestamp) VALUES ('%s', %d, '%s', %d)";
-  $resuld = db_query($query, $args);
-  $mid = db_last_insert_id('pm_message', 'mid');
-  $message['mid'] = $mid;
-
-  // Thread ID is the same as the mid if it's the first message in the thread.
-  if (!isset($message['thread_id'])) {
-    $message['thread_id'] = $mid;
-  }
-  // 2) Save message to recipients.
-  // Each recipient gets a record in the pm_index table.
-  $query = "INSERT INTO {pm_index} (mid, thread_id, uid, is_new, deleted) VALUES (%d, %d, %d, %d, 0)";
-  foreach ($message['recipients'] as $recipient) {
-    $mid = $message['mid'];
-    $thread_id  = $message['thread_id'];
-    db_query($query, $mid, $thread_id, $recipient->uid, 1);
+  if (_privatemsg_send($form_state['validate_built_message'])) {
+    drupal_set_message(t('A message has been sent to @recipients.', array('@recipients' => $form_state['values']['recipient'])));
   }
-  // Also add a record for tha author to the pm_index table - set  column "new" to 0.
-  db_query($query, $mid, $thread_id, $message['author']->uid, 0);
-
-  drupal_set_message(t('A message has been sent to @recipients.', array('@recipients' => $form_state['values']['recipient'])));
 }
 
 function pm_preview($form, &$form_state) {
@@ -906,10 +888,10 @@ function privatemsg_user($op, &$edit, &$
 
   switch ($op) {
     case 'view':
-      if (user_access('write privatemsg') && $user->uid <> $account->uid) {
+      if ($url = privatemsg_get_link($account)) {
         $account->content['privatemsg_send_new_message'] = array(
           '#type'   => 'markup',
-          '#value'  => l(t('Send this user a message'), 'messages/new/'. $account->uid, array('query' => drupal_get_destination())),
+          '#value'  => l(t('Send this user a message'), $url, array('query' => drupal_get_destination())),
           '#weight' => 10,
         );
       }
@@ -1082,31 +1064,192 @@ function privatemsg_delete($form_state, 
   );
 }
 
+function privatemsg_message_delete($pmid, $account = NULL) {
+  if (is_null($account)) {
+    global $user;
+    $account = drupal_clone($user);
+  }
+  $message = _privatemsg_load($pmid, $account->uid);
+
+  db_query('UPDATE {pm_index} SET deleted = %d WHERE mid = %d AND uid = %d', 1, $pmid, $account->uid);
+
+  $result = db_query("SELECT MIN(deleted) AS deleted_by_all FROM {pm_index} WHERE mid = %d", $pmid);
+  $deleted = db_fetch_array($result);
+
+  $deleted_by_all = FALSE;
+  if ($deleted['deleted_by_all'] == 0) {
+    $deleted_by_all = TRUE;
+  }
+
+  module_invoke_all('privatemsg_message_delete', $message, $deleted_by_all);
+}
+
 function privatemsg_delete_submit($form, &$form_state) {
-  global $user;
   $deleted = 1;
 
   if ($form_state['values']['confirm']) {
-    db_query('UPDATE {pm_index} SET deleted = %d WHERE mid = %d AND uid = %d', $deleted, $form_state['values']['pmid'], $user->uid);
+    privatemsg_message_delete($form_state['values']['pmid']);
     drupal_set_message(t('Message has been deleted'));
   }
   $form_state['redirect'] = 'messages';
 }
 
-function privatemsg_per_message_actions($pmid) {
-  $output = '';
-  $result = module_invoke_all('privatemsg_pm_controls', $pmid);
+function privatemsg_new_thread($subject, $body, $recipients, $author = NULL) {
+  if ($author == NULL) {
+    global $user;
+    $author = drupal_clone($user);
+  }
 
-  if (count($result)) {
-    $output = '<ul><li>';
-    $output .= implode('</li><li>', $result);
-    $output .= '</li></ul>';
+  $message = array();
+  $message['subject'] = $subject;
+  $message['author'] = $author;
+  $message['body'] = $body;
+  $message['timestamp'] = time();
+  $message['recipients'] = $recipients;
+
+  $errors = _privatemsg_validate_message($message, $author);
+  if (!empty($errors)) {
+    return $errors;
+  }
+  else {
+    return _privatemsg_send($message);
+  }
+}
+
+function privatemsg_answer($thread_id, $body, $author = NULL) {
+  if ($author == NULL) {
+    global $user;
+    $author = drupal_clone($user);
+  }
+
+  $message = array();
+  $message['author'] = $author;
+  $message['body'] = $body;
+  $message['timestamp'] = time();
+  $message['thread_id'] = $thread_id;
+
+  // We don't know the subject and the recipients, so we need to load them..
+  // thread_id == mid on the first message of the thread
+  $first_message = _privatemsg_load($thread_id, $author->uid);
+  if (!$first_message) {
+    return array(t('Thread %thread_id not found, unable to answer', array('%thread_id' => $thread_id)));
   }
 
-  return $output;
+
+  $query = _privatemsg_assemble_query('privatemsg_participants', $thread_id);
+  $participants = db_query($query['query']);
+  while ($result = db_fetch_object($participants)) {
+    $recipient = user_load($result->uid);
+      $message['recipients'][$recipient->uid] = $recipient;
+  }
+
+  $message['subject'] = $first_message['subject'];
+
+  $errors = _privatemsg_validate_message($message, $author);
+  if (!empty($errors)) {
+    return $errors;
+  }
+  else {
+    return _privatemsg_send($message);
+  }
 }
 
-function privatemsg_privatemsg_pm_controls($pmid) {
-  return l(t('Delete message'), 'messages/delete/'. $pmid, array('attributes' => array('class' => 'message-delete')));
+function _privatemsg_validate_message($message, $author, $show_warnings = FALSE) {
+
+  $errors = array();
+  if (!user_access('write privatemsg', $author)) {
+    // no need to do further checks in this case...
+    return array(t('User @user is not allowed to write messages', array('@user' => $author->name)));
+  }
+
+  if (empty($message['subject'])) {
+    $errors[] = t('Disallowed to send a message without subject');
+  }
+
+  $trimmed = trim(strip_tags($message['body']));
+  if (empty($trimmed)) {
+    $errors[] = t('Blank messages are not allowed');
+  }
+
+  if (empty($message['recipients']) || !is_array($message['recipients'])) {
+    $errors[] = t('Disallowed to send a message without atleast one recipient');
+  }
+
+  if (!empty($message['recipients']) && is_array($message['recipients'])) {
+    foreach ($message['recipients'] as $uid => $recipient) {
+      $block_results = module_invoke_all('privatemsg_block_message', $author, $recipient);
+      if (count($block_results) > 0) {
+        unset($message['recipients'][$uid]);
+        if ($show_warnings) {
+          foreach ($block_results as $block_result) {
+            drupal_set_message($block_result);
+          }
+        }
+      }
+    }
+  }
+
+  // Check again, give another error message if all recipients are blocked
+  if (empty($message['recipients'])) {
+    $errors[] = t('Disallowed to send message because all recipients are blocked');
+  }
+
+  return $errors += module_invoke_all('privatemsg_message_validate', $message);
 }
 
+function _privatemsg_send($message) {
+
+  drupal_alter('privatemsg_message_presave', $message);
+
+  // 1) Save the message body first.
+  $args = array();
+  $args[] = $message['subject'];
+  $args[] = $message['author']->uid;
+  $args[] = $message['body'];
+  $args[] = $message['timestamp'];
+  $query = "INSERT INTO {pm_message} (subject, author, body, timestamp) VALUES ('%s', %d, '%s', %d)";
+  $resuld = db_query($query, $args);
+  $mid = db_last_insert_id('pm_message', 'mid');
+  $message['mid'] = $mid;
+
+  // Thread ID is the same as the mid if it's the first message in the thread.
+  if (!isset($message['thread_id'])) {
+    $message['thread_id'] = $mid;
+  }
+
+  // 2) Save message to recipients.
+  // Each recipient gets a record in the pm_index table.
+
+  $query = "INSERT INTO {pm_index} (mid, thread_id, uid, is_new, deleted) VALUES (%d, %d, %d, %d, 0)";
+  foreach ($message['recipients'] as $recipient) {
+    $mid = $message['mid'];
+    db_query($query, $mid, $message['thread_id'], $recipient->uid, 1);
+  }
+  // Also add a record for tha author to the pm_index table - set  column "new" to 0.
+  db_query($query, $mid, $message['thread_id'], $message['author']->uid, 0);
+
+  module_invoke_all('privatemsg_message_insert', $message);
+
+  return TRUE;
+}
+
+function privatemsg_get_link($recipient, $account = NULL) {
+  if ($account == NULL) {
+    global $user;
+    $account = $user;
+  }
+
+  if (!user_access('write privatemsg', $account)
+      || !user_access('read privatemsg', $recipient)
+      || $recipient->uid == 0
+      || $account->uid == 0
+      || $recipient->uid == $account->uid) {
+    return FALSE;
+  }
+
+  if (count(module_invoke_all('privatemsg_block_message', $account, $recipient)) > 0) {
+    return FALSE;
+  }
+
+  return 'messages/new/'. $recipient->uid;
+}
Index: privatemsg-view.tpl.php
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg-view.tpl.php,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 privatemsg-view.tpl.php
--- privatemsg-view.tpl.php	19 Dec 2008 06:09:50 -0000	1.1.2.2
+++ privatemsg-view.tpl.php	26 Jan 2009 20:35:10 -0000
@@ -23,7 +23,11 @@
     </div>
     <?php if ( isset($message_actions) ) : ?>
       <div class="message-actions">
-        <?php print $message_actions; ?>
+        <ul>
+          <?php foreach ($message_actions as $action): ?>
+            <li><?php print $action ?></li>
+          <?php endforeach; ?>
+        </ul>
       </div>
     <?php endif ?>
   </div>
Index: privatemsg.author-pane.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/Attic/privatemsg.author-pane.inc,v
retrieving revision 1.1.2.1
diff -u -p -r1.1.2.1 privatemsg.author-pane.inc
--- privatemsg.author-pane.inc	6 Jan 2009 16:05:48 -0000	1.1.2.1
+++ privatemsg.author-pane.inc	26 Jan 2009 20:35:10 -0000
@@ -10,18 +10,15 @@
  * Implementation of hook_preprocess_author_pane().
  */
 function privatemsg_preprocess_author_pane(&$variables) {
-  global $user;
-  $account_id = $variables['account']->uid;
   $image_path = $variables['image_path'];
-
   // Send private message
-  if ($account_id != 0 && $account_id != $user->uid && user_access('write privatemsg')) {
+  if ($url = privatemsg_get_link($variables['account'])) {
     $variables['privatemsg'] =
       l(theme('image', "$image_path/private-message.png",
               t('Send private message'), t('Send private message'), NULL, TRUE),
-        "messages/new/$account_id",
+        $url,
         array('absolute' => TRUE, 'html' => TRUE));
 
-    $variables['privatemsg_link'] = l(t('Send PM'), 'messages/new/'. $account_id);
+    $variables['privatemsg_link'] = l(t('Send PM'), $url);
   }
 }
Index: privatemsgapi/privatemsgapi.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsgapi/privatemsgapi.inc,v
retrieving revision 1.8.2.1
diff -u -p -r1.8.2.1 privatemsgapi.inc
--- privatemsgapi/privatemsgapi.inc	26 Jan 2009 19:53:27 -0000	1.8.2.1
+++ privatemsgapi/privatemsgapi.inc	26 Jan 2009 20:35:11 -0000
@@ -8,7 +8,7 @@
  */
 
 /**
- * Message sending helper functions
+ * @file Message sending helper functions
  */
 /**
  * Saves message to group association
@@ -60,10 +60,13 @@ function _save_privatemsg_group_user($gi
  */
 function _privatemsg_load($pmid, $uid = NULL) {
   $query = _privatemsg_assemble_query('privatemsg_load', $pmid, $uid);
-//  drupal_set_message('<pre>'. print_r($query, 1) . '</pre>');
 
   $result = db_query($query['query']);
   $message = db_fetch_array($result);
+  $returned = module_invoke_all('privatemsg_message_load', $message);
+  if (!empty($returned)) {
+    $message = array_merge_recursive($returned, $message);
+  }
   return $message;
 }
 
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	26 Jan 2009 20:35:11 -0000
@@ -97,23 +97,32 @@ function pm_block_user_form_submit($form
   }
 }
 
+function pm_block_user_privatemsg_load_alter(&$fragments, $pmid, $uid) {
+  $fragments['select'][] = 'pmbu.recipient AS is_blocked';
+  $fragments['select'][]      = "pmi.thread_id";
+
+  $fragments['inner_join'][] = 'LEFT JOIN {pm_block_user} pmbu ON (pm.author = pmbu.author AND pmi.uid = pmbu.recipient)';
+}
+
 /**
  * Implementation of hook_privatemsg_pm_controls.
  */
- function pm_block_user_privatemsg_pm_controls($pmid) {
+ function pm_block_user_privatemsg_message_view_alter(&$vars) {
   global $user;
-  $author_id = db_result(db_query('SELECT author FROM {pm_message} WHERE mid = %d', $pmid));
-  $thread = db_result(db_query('SELECT thread_id FROM {pm_index} WHERE mid = %d', $pmid));
+
+  $author_id = $vars['message']['author']->uid;
+  if (!isset($vars['message']['thread_id'])) {
+    // No thread id, this is probably only a preview
+    return;
+  }
+  $thread_id = $vars['message']['thread_id'];
 
   if ($user->uid <> $author_id) {
-    if (db_result(db_query('SELECT COUNT(recipient) FROM {pm_block_user} WHERE author = %d AND recipient = %d', $author_id, $user->uid))) {
-      $output = l(t('Unblock author'), 'messages/block/'. $author_id, array('query' => 'destination=messages/view/' . $thread));
+    if ($vars['message']['is_blocked']) {
+      $vars['message_actions'][] = l(t('Unblock author'), 'messages/block/'. $author_id, array('query' => 'destination=messages/view/' . $thread_id));
     }
     else {
-      $output = l(t('Block author'), 'messages/block/'. $author_id, array('query' => 'destination=messages/view/' . $thread));
+      $vars['message_actions'][] = l(t('Block author'), 'messages/block/'. $author_id, array('query' => 'destination=messages/view/' . $thread_id));
     }
   }
-  if (isset($output)) {
-    return $output;
-  }
 }
\ No newline at end of file
