I know that I have seen this request in the issue queue before, but

  1. I honestly can't find it anymore
  2. back then nobody had a clue how to achieve this.

I would love to see the feature (certainly optional) to maintain only a single thread between any two users or any given set of participants. Just like Facebook has it. This would be really nice for all of us who use Privatemsg more as a chat module than as a proper email module.

Anil Maharjan has posted a patch for Privatemsg on his blog that would take care of exactly this behavior: http://anilmaharjan.com.np/blog/2013/05/maintain-single-thread-between-t...

Could one of the maintainers have a look if this could be implemented in Privatemsg module?

Comments

The patch linked on the blog is not an optional setting; it needs to be rewritten in a way that makes it an optional setting and also tests need to be supplied. Perhaps the author of the patch would be interested in contributing it back? Contributions are always welcome.

I looked into the patch today but I noticed another problem. Since the patch adds a check upon message save, if the author has already sent a message to the recipient before and is sending the message from the new message screen, he or she will be prompted to input a subject, which will be ignored.

I think a better way to handle this is, if there is already a thread of messages, redirect the user to that thread when they click the "Send a message" link. Here's some sample code to do this based on the patch example:

In privatemsg.module:

*** 2006,2016 ****
    if (empty($validated)) {
      return FALSE;
    }
!   $url = 'messages/new/' . implode(',', $validated);
!   if (!is_null($subject)) {
!     // Explicitly encode the / so that it will be encoded twice to work around
!     // the the menu_system.
!     $url .= '/' . str_replace('/', '%2F', $subject);
    }
    return $url;
  }
--- 2009,2029 ----
    if (empty($validated)) {
      return FALSE;
    }
!   // http://anilmaharjan.com.np/blog/2013/05/maintain-single-thread-between-two-users-in-privatemsg-module
!   $res = db_query("select author, recipient, thread_id , name from {pm_message} msg
!   left join {pm_index} ind on msg.mid = ind.mid
!   left join {users} u on u.uid = ind.recipient
!   where recipient = :recipient and author = :uid",array(':recipient'=>$recipient->recipient, ':uid' => $account->uid));
!   if ($thread_id = $res->fetchObject()->thread_id) {
!     $url = 'messages/view/' . $thread_id . '#replyform';
!   }
!   else {
!     $url = 'messages/new/' . implode(',', $validated);
!     if (!is_null($subject)) {
!       // Explicitly encode the / so that it will be encoded twice to work around
!       // the the menu_system.
!       $url .= '/' . str_replace('/', '%2F', $subject);
!     }
    }
    return $url;
  }

And in privatemsg.pages.module:

*** 429,435 ****
      '#default_value' => $thread['subject'],
    );
    $form['reply'] = array(
!     '#markup' =>  '<h2 class="privatemsg-reply">' . t('Reply') . '</h2>',
      '#weight' => -10,
    );
--- 429,435 ----
      '#default_value' => $thread['subject'],
    );
    $form['reply'] = array(
!     '#markup' =>  '<h2 class="privatemsg-reply" id="replyform">' . t('Reply') . '</h2>',
      '#weight' => -10,
    );
 

This is just an example and should be tested extensively before production use.

I guess that if each set (pair or group) of users can only have a single conversation thread then subjects no longer make sense; the topic of the conversation will eventually vary. I kinda think that enabling the option "store all messages in a single conversation thread" should remove subjects from private messages. It would then be feasible to display a teaser of the latest message in the message overview instead of a proper subject. This would exactly be the way that Facebook handles private messaging.

Edit: Privatemsg module already has the feature that if the subject field is left blank then a teaser of the message is displayed instead, so all one would need to do is set $form['subject']['#access']= FALSE;.

Edit2: Also, ':recipient'=>$recipient->recipient throws errors, I have already tested this. It needs to be replaced with ':recipient'=>reset($message->recipients)->uid.

Hey Sebastian @semei,

Thanx for the approach. I am more than happy if I can be of some help.

Anil Maharjan

Issue summary:View changes

typo

After testing a bunch I found the code provided by @ptmkenny does not find threads with only messages sent by the other participant, or if the user deleted all the messages in the thread (resulting in a thread page populated with all current user's messages from all threads).

I have improved the query, and to make things easier I have created a function that finds the first existing thread between any two users, accounting for deleted messages by $participant_a and no messages sent at all by either participants.

<?php
/**
* Custom function for finding an existing thread between two users
*/
function mymodule_existing_pm_thread($participant_a, $participant_b) {
 
// Find existing thread
 
$result = db_query("SELECT msg.author, ind.recipient, ind.thread_id
   FROM {pm_message} msg
   LEFT JOIN {pm_index} ind on msg.mid = ind.mid
   CROSS JOIN {pm_index} ind2 ON ind.thread_id = ind2.thread_id AND ind2.recipient = :a AND ind2.deleted = 0
   WHERE ((msg.author = :a AND ind.recipient = :b) OR (msg.author = :b AND ind.recipient = :a))"
,array(':a' => $participant_a, ':b' => $participant_b));
 
$result_object = $result->fetchObject();
 
// If found thread
 
if(!empty($result_object->thread_id)) {
   
$thread_id = $result_object->thread_id;
    return
$thread_id;
  }
  return
NULL;
}
?>

This can then be used in the user page pm link as such:

<?php
function mymodule_user_view($account) {
  global
$user;
 
$existing_thread = mymodule_existing_pm_thread($user->uid, $account->uid);
  if (!empty(
$existing_thread) && !empty($account->content['privatemsg_send_new_message']['#href'])) {
   
$account->content['privatemsg_send_new_message']['#href'] = 'messages/view/' . $existing_thread;
  }
}
?>

Thanks nyariv! This is a more robust solution than the one I posted.

I've taken the liberty of adding your suggestion (with credit to you) to the documentation here:
https://drupal.org/node/2135543

This feature could be added as a patch to the module as well, but for now, I think the custom module solution is a good one.