? .cvsignore
? new_reply_refactor.patch
Index: privatemsg.pages.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.pages.inc,v
retrieving revision 1.25
diff -u -p -r1.25 privatemsg.pages.inc
--- privatemsg.pages.inc	17 Jan 2011 10:37:53 -0000	1.25
+++ privatemsg.pages.inc	20 Jan 2011 19:36:44 -0000
@@ -170,7 +170,7 @@ function privatemsg_view($thread) {
 
   // Display the reply form if user is allowed to use it.
   if (privatemsg_user_access('write privatemsg') || privatemsg_user_access('reply only privatemsg')) {
-    $content['reply']['#value'] = drupal_get_form('privatemsg_new', $thread['participants'], $thread['subject'], $thread['thread_id'], $thread['read_all']);
+    $content['reply']['#value'] = drupal_get_form('privatemsg_form_reply', $thread);
     $content['reply']['#weight'] = 5;
   }
 
@@ -186,96 +186,161 @@ function privatemsg_view($thread) {
   return drupal_render($content);
 }
 
-
-function privatemsg_new(&$form_state, $recipients = array(), $subject = '', $thread_id = NULL, $read_all = FALSE) {
+/**
+ * Form builder function; Write a new private message.
+ */
+function privatemsg_new(&$form_state, $recipients = '', $subject = '') {
   global $user;
 
-  $recipients_string = '';
-  $recipients_plain = '';
-  $body      = '';
-
-  // convert recipients to array of user objects
+  // Convert recipients to array of user objects.
   $unique = FALSE;
   if (!empty($recipients) && is_string($recipients) || is_int($recipients)) {
     $unique = TRUE;
     $recipients = _privatemsg_generate_user_array($recipients);
-  }
-  elseif (is_object($recipients)) {
-    $recipients = array($recipients);
-  }
-  elseif (empty($recipients) && is_string($recipients)) {
+  } else {
     $recipients = array();
   }
 
-  $usercount = 0;
-  $to = array();
-  $to_plain = array();
-  $blocked_messages = array();
-  foreach ($recipients as $recipient) {
-    // Allow to pass in normal user objects.
-    if (empty($recipient->type)) {
-      $recipient->type = 'user';
-      $recipient->recipient = $recipient->uid;
-    }
-    if ($recipient->type == 'hidden') {
-      continue;
-    }
-    if (isset($to[privatemsg_recipient_key($recipient)])) {
-      // We already added the recipient to the list, skip him.
-      continue;
-    }
-    if (!privatemsg_recipient_access($recipient->type, 'write', $recipient)) {
-      // User does not have access to write to this recipient, continue.
-      continue;
+  if (isset($form_state['values'])) {
+    if (isset($form_state['values']['recipient'])) {
+      $recipients_plain = $form_state['values']['recipient'];
     }
+    $subject   = $form_state['values']['subject'];
+  }
+  else {
+    $to = _privatemsg_get_allowed_recipients($recipients);
 
-    // Check if another module is blocking the sending of messages to the recipient by current user.
-    $user_blocked = module_invoke_all('privatemsg_block_message', $user, array(privatemsg_recipient_key($recipient) => $recipient), array('thread_id' => $thread_id));
-    if (!count($user_blocked) <> 0 && $recipient->recipient) {
-      if ($recipient->type == 'user' && $recipient->recipient == $user->uid) {
-        $usercount++;
-        // Skip putting author in the recipients list for now.
-        continue;
+    $recipients_plain = '';
+    if (!empty($to)) {
+      $to_plain = array();
+      $to_title = array();
+      foreach ($to as $recipient) {
+        $to_plain[] = privatemsg_recipient_format($recipient, array('plain' => TRUE, 'unique' => $unique));
+        $to_title[] = privatemsg_recipient_format($recipient, array('plain' => TRUE));
       }
-      $to[privatemsg_recipient_key($recipient)] = privatemsg_recipient_format($recipient);
-      $to_plain[privatemsg_recipient_key($recipient)] = privatemsg_recipient_format($recipient, array('plain' => TRUE, 'unique' => $unique));
+      $recipients_plain = implode(', ', $to_plain);
+      $recipients_title = implode(', ', $to_title);
     }
-    else {
-      // Store blocked messages. These are only displayed if all recipients
-      // are blocked.
-      $first_reason = reset($user_blocked);
-      $blocked_messages[] = $first_reason['message'];
+  }
+
+  if (!empty($recipients_title)) {
+    drupal_set_title(t('Write new message to %recipient', array('%recipient' => $recipients_title)));
+  }
+  else {
+    drupal_set_title(t('Write new message'));
+  }
+
+  $form = array(
+    '#access' => privatemsg_user_access('write privatemsg'),
+  );
+
+  $form += _privatemsg_form_base_fields($form_state);
+
+  $description_array = array();
+  foreach (privatemsg_recipient_get_types() as $name => $type) {
+    if (privatemsg_recipient_access($name, 'write')) {
+      $description_array[] = $type['description'];
     }
   }
+  $description = t('Enter the recipient, separate recipients with commas.');
+  $description .= theme('item_list', array('items' => $description_array));
+
+  $form['recipient'] = array(
+    '#type'               => 'textfield',
+    '#title'              => t('To'),
+    '#description'        => $description,
+    '#default_value'      => $recipients_plain,
+    '#required'           => TRUE,
+    '#weight'             => -10,
+    '#size'               => 50,
+    '#autocomplete_path'  => 'messages/autocomplete',
+    // Do not hardcode #maxlength, make it configurable by number of recipients, not their name length.
+  );
+  $form['subject'] = array(
+    '#type'               => 'textfield',
+    '#title'              => t('Subject'),
+    '#size'               => 50,
+    '#maxlength'          => 255,
+    '#default_value'      => $subject,
+    '#weight'             => -5,
+  );
 
-  if (empty($to) && $usercount >= 1 && empty($blocked_messages)) {
-    // Assume the user sent message to own account as if the usercount is one or less, then the user sent a message but not to self.
-    $to['user_' . $user->uid] = privatemsg_recipient_format($user);
-    $to_plain['user_' . $user->uid] = privatemsg_recipient_format($user, array('plain' => TRUE));
+  $url = privatemsg_get_dynamic_url_prefix();
+  if (isset($_REQUEST['destination'])) {
+    $url = $_REQUEST['destination'];
   }
 
+  $form['cancel'] = array(
+    '#value'  => l(t('Cancel'), $url, array('attributes' => array('id' => 'edit-cancel'))),
+    '#weight' => 20,
+  );
+
+  return $form;
+}
+
+/**
+ * Form builder function; Write a reply to a thread.
+ */
+function privatemsg_form_reply(&$form_state, $thread) {
+  $form = array(
+    '#access' => privatemsg_user_access('write privatemsg') || privatemsg_user_access('reply only privatemsg'),
+  );
+
+  $to = _privatemsg_get_allowed_recipients($thread['participants'], $thread['thread_id']);
   if (!empty($to)) {
-    $recipients_string = implode(', ', $to);
-    $recipients_plain = implode(', ', $to_plain);
+    $recipients = _privatemsg_format_participants($to);
   }
-  if (isset($form_state['values'])) {
-    if (isset($form_state['values']['recipient'])) {
-      $recipients_plain = $form_state['values']['recipient'];
+  else {
+    // Display a message if some users are blocked.
+    // @todo: Move this check out of the form, don't use the form in that case.
+    if (count(_privatemsg_blocked_messages())) {
+      $blocked = t('You can not reply to this conversation because all recipients are blocked.');
+      $blocked .= theme('item_list', _privatemsg_blocked_messages());
+      $form['blocked']['#value'] = $blocked;
     }
-    $subject   = $form_state['values']['subject'];
-    $body      = $form_state['values']['body'];
-  }
-  if (!$thread_id && !empty($recipients_plain)) {
-    drupal_set_title(t('Write new message to %recipient', array('%recipient' => $recipients_plain)));
-  }
-  elseif (!$thread_id) {
-    drupal_set_title(t('Write new message'));
+    else {
+      $form['#access'] = FALSE;
+    }
+    return $form;
   }
 
-  $form = array(
-    '#type'               => 'fieldset',
-    '#access'             => privatemsg_user_access('write privatemsg') || privatemsg_user_access('reply only privatemsg'),
+  $form += _privatemsg_form_base_fields($form_state);
+
+  $form['cancel'] = array(
+    '#value'  => l(t('Clear'), $_GET['q'], array('attributes' => array('id' => 'edit-cancel'))),
+    '#weight' => 20,
+  );
+
+  $form['thread_id'] = array(
+    '#type' => 'value',
+    '#value' => $thread['thread_id'],
   );
+  $form['subject'] = array(
+    '#type' => 'value',
+    '#default_value' => $thread['subject'],
+  );
+  $form['reply'] = array(
+    '#value' =>  '<h2 class="privatemsg-reply">' . t('Reply') . '</h2>',
+    '#weight' => -10,
+  );
+  $form['recipient_display'] = array(
+    '#value' =>  '<p>'. t('<strong>Reply to thread</strong>:<br /> Recipients: !to', array('!to' => $recipients)) .'</p>',
+    '#weight' => -10,
+  );
+
+  $form['read_all'] = array(
+    '#type'  => 'value',
+    '#value' => $thread['read_all'],
+  );
+  return $form;
+}
+
+/**
+ * Returns the common fields of the reply and new form.
+ */
+function _privatemsg_form_base_fields(&$form_state) {
+  global $user;
+
   if (isset($form_state['privatemsg_preview'])) {
     $form['message_header'] = array(
       '#type' => 'fieldset',
@@ -287,48 +352,20 @@ function privatemsg_new(&$form_state, $r
       '#value'  => $form_state['privatemsg_preview'],
     );
   }
+
   $form['author'] = array(
     '#type' => 'value',
     '#value' => $user,
   );
-  if (is_null($thread_id)) {
-    $description_array = array();
-    foreach (privatemsg_recipient_get_types() as $name => $type) {
-      if (privatemsg_recipient_access($name, 'write')) {
-        $description_array[] = $type['description'];
-      }
-    }
-    $description = t('Enter the recipient, separate recipients with commas.');
-    $description .= theme('item_list', array('items' => $description_array));
 
-    $form['recipient'] = array(
-      '#type'               => 'textfield',
-      '#title'              => t('To'),
-      '#description'        => $description,
-      '#default_value'      => $recipients_plain,
-      '#required'           => TRUE,
-      '#weight'             => -10,
-      '#size'               => 50,
-      '#autocomplete_path'  => 'messages/autocomplete',
-      // Do not hardcode #maxlength, make it configurable by number of recipients, not their name length.
-    );
-  }
-  $form['subject'] = array(
-    '#type'               => 'textfield',
-    '#title'              => t('Subject'),
-    '#size'               => 50,
-    '#maxlength'          => 255,
-    '#default_value'      => $subject,
-    '#weight'             => -5,
-  );
   $form['body'] = array(
     '#type'               => 'textarea',
     '#title'              => t('Message'),
     '#rows'               => 6,
     '#weight'             => -3,
-    '#default_value'      => $body,
     '#resizable'          => TRUE,
   );
+
   $format = FILTER_FORMAT_DEFAULT;
   // The input filter widget looses the format during preview, specify it
   // explicitly.
@@ -339,75 +376,90 @@ function privatemsg_new(&$form_state, $r
   $form['format']['#access'] = privatemsg_user_access('select text format for privatemsg');
   if (variable_get('privatemsg_display_preview_button', FALSE)) {
     $form['preview'] = array(
-      '#type'               => 'submit',
-      '#value'              => t('Preview message'),
-      '#submit'             => array('privatemsg_new_preview'),
-      '#weight'             => 10,
+      '#type'     => 'submit',
+      '#value'    => t('Preview message'),
+      '#validate' => array('privatemsg_new_validate'),
+      '#submit'   => array('privatemsg_new_preview'),
+      '#weight'   => 10,
     );
   }
+
   $form['submit'] = array(
-    '#type'               => 'submit',
-    '#value'              => t('Send message'),
-    '#weight'             => 15,
+    '#type'     => 'submit',
+    '#value'    => t('Send message'),
+    '#weight'   => 15,
+    '#validate' => array('privatemsg_new_validate'),
+    '#submit'   => array('privatemsg_new_submit'),
   );
-  $url = privatemsg_get_dynamic_url_prefix();
-  $title = t('Cancel');
-  if (isset($_REQUEST['destination'])) {
-    $url = $_REQUEST['destination'];
-  }
-  elseif (!is_null($thread_id)) {
-    $url = $_GET['q'];
-    $title = t('Clear');
-  }
 
-  $form['cancel'] = array(
-    '#value'  => l($title, $url, array('attributes' => array('id' => 'edit-cancel'))),
-    '#weight' => 20,
-  );
+  return $form;
+}
 
-  if (!is_null($thread_id)) {
-    $form['thread_id'] = array(
-      '#type' => 'value',
-      '#value' => $thread_id,
-    );
-    $form['subject'] = array(
-      '#type' => 'value',
-      '#default_value' => $subject,
-    );
-    $form['reply'] = array(
-      '#value' =>  '<h2 class="privatemsg-reply">' . t('Reply') . '</h2>',
-      '#weight' => -10,
-    );
-    $recipients_string_themed = implode(', ', $to);
-    if (!empty($recipients_string_themed)) {
-      $form['recipient_display'] = array(
-        '#value' =>  '<p>'. t('<strong>Reply to thread</strong>:<br /> Recipients: !to', array('!to' => $recipients_string_themed)) .'</p>',
-        '#weight' => -10,
-      );
+/**
+ * Check if the current user is allowed to write these recipients.
+ *
+ * @param $recipients
+ *   Array of recipient objects.
+ *
+ * @return
+ *   Array of allowed recipient objects.
+ */
+function _privatemsg_get_allowed_recipients($recipients, $thread_id = NULL) {
+  global $user;
+
+  $usercount = 0;
+  $valid = array();
+  $blocked_messages = &_privatemsg_blocked_messages();
+  $blocked_messages = array();
+  foreach ($recipients as $recipient) {
+    // Allow to pass in normal user objects.
+    if (empty($recipient->type)) {
+      $recipient->type = 'user';
+      $recipient->recipient = $recipient->uid;
+    }
+    if ($recipient->type == 'hidden') {
+      continue;
+    }
+    if (isset($valid[privatemsg_recipient_key($recipient)])) {
+      // We already added the recipient to the list, skip him.
+      continue;
+    }
+    if (!privatemsg_recipient_access($recipient->type, 'write', $recipient)) {
+      // User does not have access to write to this recipient, continue.
+      continue;
     }
-    if (empty($recipients_string)) {
-      // If there are no valid recipients, hide all visible parts of the form.
-      foreach (element_children($form) as $element) {
-        $form[$element]['#access'] = FALSE;
-      }
 
-      // Display a message if some users are blocked.
-      if (!empty($blocked_messages)) {
-        $blocked = t('You can not reply to this conversation because all recipients are blocked.');
-        $blocked .= theme('item_list', $blocked_messages);
-        $form['blocked']['#value'] = $blocked;
-      }
-      // If there are no valid recipients, unset the message reply form.
-      $form['#access'] = FALSE;
+    if ($recipient->type == 'user' && $recipient->recipient == $user->uid) {
+      // Skip putting author in the recipients list for now.
+      // Will be added if he is the only recipient.
+      $usercount++;
+      continue;
     }
+    $valid[privatemsg_recipient_key($recipient)] = $recipient;
   }
-  // Only set read all if it is a boolean TRUE. It might also be an integer set
-  // through the URL.
-  $form['read_all'] = array(
-    '#type'  => 'value',
-    '#value' => $read_all === TRUE,
-  );
-  return $form;
+
+  foreach (module_invoke_all('privatemsg_block_message', $user, $valid, array('thread_id' => $thread_id)) as $blocked) {
+    // Unset the recipient.
+    unset($valid[$blocked['recipient']]);
+    // Store blocked messages. These are only displayed if all recipients
+    // are blocked.
+    $blocked_messages[] = $blocked['message'];
+  }
+
+  if (empty($valid) && $usercount >= 1 && empty($blocked_messages)) {
+    // Assume the user sent message to own account as if the usercount is one or
+    // less, then the user sent a message but not to self.
+    $valid['user_' . $user->uid] = $user;
+  }
+  return $valid;
+}
+
+/**
+ * Static storage for blocked messages.
+ */
+function &_privatemsg_blocked_messages() {
+  static $blocked_messages = array();
+  return $blocked_messages;
 }
 
 function privatemsg_new_validate($form, &$form_state) {
Index: privatemsg.test
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg.test,v
retrieving revision 1.29
diff -u -p -r1.29 privatemsg.test
--- privatemsg.test	27 Dec 2010 09:10:06 -0000	1.29
+++ privatemsg.test	20 Jan 2011 19:36:44 -0000
@@ -698,7 +698,6 @@ class PrivatemsgTestCase extends DrupalW
     );
     $this->drupalLogin($user);
     $this->drupalPost('messages/new', $message, t('Preview message'));
-    $this->assertTitle(t('Write new message to @user', array('@user' => $user->name)) . ' | Drupal', t('Correct title is displayed.'));
     $this->assertFieldByXPath("//div[@class='privatemsg-message-body']/p", $message['body'], t('Message body is previewed'));
   }
 
Index: privatemsg_attachments/privatemsg_attachments.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg_attachments/privatemsg_attachments.module,v
retrieving revision 1.8
diff -u -p -r1.8 privatemsg_attachments.module
--- privatemsg_attachments/privatemsg_attachments.module	27 Nov 2010 15:12:38 -0000	1.8
+++ privatemsg_attachments/privatemsg_attachments.module	20 Jan 2011 19:36:44 -0000
@@ -16,6 +16,21 @@ function privatemsg_attachments_perm() {
 /**
  * Implements hook_form_FORM_ID_alter().
  */
+function privatemsg_attachments_form_privatemsg_form_reply_alter(&$form, &$form_state) {
+
+  // If there are no valid recipients, the reply form possibly only shows a
+  // error message. Don't add the forward fieldset in that case.
+  if (!isset($form['submit'])) {
+
+  }
+
+  // Reply form is separate now, just forward to the existing hook.
+  privatemsg_attachments_form_privatemsg_new_alter($form, $form_state);
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
 function privatemsg_attachments_form_privatemsg_new_alter(&$form, &$form_state) {
   if (user_access('upload private message attachments')) {
     $form['attachments'] = array(
@@ -66,7 +81,11 @@ function privatemsg_attachments_form_pri
       $form['attachments']['wrapper'] += _privatemsg_attachments_form($files);
       // Execute submit function as validate, to have it executed before
       // $form_state['validate_built_message'] is created.
-      array_unshift($form['#validate'], '_privatemsg_attachments_upload_submit');
+      array_unshift($form['submit']['#validate'], '_privatemsg_attachments_upload_submit');
+      if (isset($form['preview'])) {
+        array_unshift($form['preview']['#validate'], '_privatemsg_attachments_upload_submit');
+      }
+
       $form['#attributes']['enctype'] = 'multipart/form-data';
     }
   }
Index: privatemsg_forward/privatemsg_forward.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/privatemsg/privatemsg_forward/privatemsg_forward.module,v
retrieving revision 1.1
diff -u -p -r1.1 privatemsg_forward.module
--- privatemsg_forward/privatemsg_forward.module	15 Jan 2011 11:40:57 -0000	1.1
+++ privatemsg_forward/privatemsg_forward.module	20 Jan 2011 19:36:44 -0000
@@ -14,10 +14,12 @@ function privatemsg_forward_perm() {
 }
 
 /**
- * Implements hook_privatemsg_view_messages_alter().
+ * Implements hook_form_FORM_ID_alter().
  */
 function privatemsg_forward_form_privatemsg_new_alter(&$form, &$form_state) {
-  if (isset($form['thread_id'])) {
+  // If there are no valid recipients, the reply form possibly only shows a
+  // error message. Don't add the forward fieldset in that case.
+  if (isset($form['submit'])) {
     $form['forward'] = array(
       '#type'               => 'fieldset',
       '#access'             => privatemsg_user_access('forward a privatemsg thread'),
