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

ptmkenny’s picture

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.

ptmkenny’s picture

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.

semei’s picture

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.

dejavu1987’s picture

Hey Sebastian @semei,

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

Anil Maharjan

dejavu1987’s picture

Issue summary: View changes

typo

nyariv’s picture

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.

/**
 * 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:

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;
  }
}
ptmkenny’s picture

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.

usta’s picture

Issue summary: View changes

Keep in mind that this will only create an updated link after a page reload. This solution will not work when a user opens multiple forms in separate tabs, submitting one after another. To address this issue one might add a custom validation to the privatemsg form to check if a thread for author and recipient exists, and react accordingly, e.g. by redirecting the user to the thread, or displaying a message.

kovalevm’s picture

Please tell me in which module's files should be inserted this code?

/**
 * 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;
}

and this:

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;
  }
}
kovalevm’s picture

ivnish’s picture

Status: Active » Closed (outdated)