Email - contact case update email doesn't send in the same email thread.

version 7.11.5
php 7.3
mysql 5.5
outbound example@outlook.com
inbound example@outlook.com

Problem:
The contact case update email doesn’t send as a reply email. All contact case update email doesn’t in the same email thread, even though I insert “RE:” in front of the subject email in the email template.
It is wired if every time user reply to the same case is a different email thread.

Your screenshot looks like Gmail, not Outlook.

Gmail uses its own logic to decide whether to group emails in threads:

https://webapps.stackexchange.com/questions/965/how-does-gmail-decide-to-thread-email-messages

Maybe SuiteCRM is not sending out the “in-reply-to” header, you can check this in your emails source.

Hi pgr,

The capture was the customer inbox. Actually, my outbound is outlook.com. I using example@outlook.com sent to Gmail or Outlook also doesn’t group together at the customer inbox. If I want the auto contact case update email is sent and group together by the case. How do I add the “In-Reply-to”?

Do you know how to see an email source? Look for that option in Gmail (“show original”), and in Outlook.

The first step would be to determine what is currently happening. I don’t know this part of SuiteCRM very well and I am unsure if this is something nobody thought of before, or if it an existing feature that isn’t working as well as it should.

If you can try and look at your emails and see “who” (Gmail, Outlook, SuiteCRM) is adding “In-Reply-to” parameters, and who is respecting them, etc.

Basically each “actor” should either add that id, or pass it on when replying, or use it in order to show the messages as threaded to the user. If you can do some diagnosis of that we might understand better what’s going on.

Here’s a sample email source with a “In-Reply-to” header on the 4th line:


MIME-Version: 1.0
Date: Wed, 26 Jun 2019 14:17:54 +0100
References: <101d07a652615bcc7f96d87fb7678055@suitecrm.com>
In-Reply-To: <101d07a652615bcc7f96d87fb7678055@suitecrm.com>
Message-ID: <CALVQZjM5xEaJu=i8skpLL21Tz3g1Tu7vjWDVzQqdhRd0pTxBsA@mail.gmail.com>
Subject: Re: SuiteCRM Forums Email - contact case update email doesn't send in the same email thread. (SuiteCRM General Discussion)
From: "Name" <obfuscated@gmail.com>
To: "Name" <obfuscated@gmail.com>
Content-Type: multipart/alternative; boundary="0000000000004fe487058c39df78"

--0000000000004fe487058c39df78
Content-Type: text/plain; charset="UTF-8"

On Wed, Jun 26, 2019 at 1:48 AM SuiteCRM Forums <forum@suitecrm.com> wrote:

> A new reply has been posted on the SuiteCRM Forums
> Message Subject : Email - contact case update email doesn't send in the
> same email thread.
> Category : SuiteCRM General Discussion
> Posted by : woon
>
> URL :
> *https://suitecrm.com/suitecrm/forum/suitecrm-7-0-discussion/26536-email-contact-case-update-email-doesn-t-send-in-the-same-email-thread/86279*
> <https://suitecrm.com/suitecrm/forum/suitecrm-7-0-discussion/26536-email-contact-case-update-email-doesn-t-send-in-the-same-email-thread/86279>
> Message:
> ------------------------------
> Hi pgr,
>
> The capture was the customer inbox. Actually, my outbound is outlook.com.
> I using This email address is being protected from spambots. You need
> JavaScript enabled to view it. sent to Gmail or Outlook also doesn't
> group together at the customer inbox. If I want the auto contact case
> update email is sent and group together by the case. How do I add the
> "In-Reply-to"?
> ------------------------------
> You can unsubscribe from this topic by visiting it and clicking the
> Unsubscribe button near the top or bottom of the page.
> Do not answer to this e-mail notification as it is a generated e-mail.
>

--0000000000004fe487058c39df78
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><br></div><br><div class=3D"gmail_quote"><div dir=3D"ltr" =
class=3D"gmail_attr">On Wed, Jun 26, 2019 at 1:48 AM SuiteCRM Forums &lt;<a=
 href=3D"mailto:forum@suitecrm.com">forum@suitecrm.com</a>&gt; wrote:<br></=
div><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;bor=
der-left:1px solid rgb(204,204,204);padding-left:1ex"><h2>A new reply has b=
een posted on the SuiteCRM Forums</h2>

<div>Message Subject : Email - contact case update email doesn&#39;t send i=
n the same email thread.</div>
<div>Category : SuiteCRM General Discussion</div>
<div>Posted by : woon</div>

<p>URL : <a href=3D"https://suitecrm.com/suitecrm/forum/suitecrm-7-0-discus=
sion/26536-email-contact-case-update-email-doesn-t-send-in-the-same-email-t=
hread/86279" target=3D"_blank"><b>https://suitecrm.com/suitecrm/forum/suite=
crm-7-0-discussion/26536-email-contact-case-update-email-doesn-t-send-in-th=
e-same-email-thread/86279</b></a></p>

Message:
<hr>
<div>Hi pgr,<br>
<br>
The capture was the customer inbox. Actually, my outbound is <a href=3D"htt=
p://outlook.com" target=3D"_blank">outlook.com</a>. I using <span id=3D"gma=
il-m_3701091091206648981cloak643065558497cb4d728ca9358fd70a1b">This email a=
ddress is being protected from spambots. You need JavaScript enabled to vie=
w it.</span> sent to Gmail or Outlook also doesn&#39;t group together at th=
e customer inbox. If I want the auto contact case update email is sent and =
group together by the case. How do I add the &quot;In-Reply-to&quot;?</div>
<hr>
<div>You can unsubscribe from this topic by visiting it and clicking the Un=
subscribe button near the top or bottom of the page.</div>
<div>Do not answer to this e-mail notification as it is a generated e-mail.=
</div>


</blockquote></div>

--0000000000004fe487058c39df78--

Yup, there is no “in-reply-to” in the header.

Reproduce:

  1. User click on a case
  2. User type new message in the text box, case update thread.
  3. User click save
  4. The system saves the record and sends an email to the contact.

User doesn’t compose the email. The system is the one who auto composes the email. It seems like the system doesn’t compose the email as a reply email or doesn’t add “in-reply-to” in the header.

You say:
If you can try and look at your emails and see “who” (Gmail, Outlook, SuiteCRM) is adding “In-Reply-to” parameters, and who is respecting them, etc.

I do know how to see who is respecting to add “In-Reply-to”. I am giving you some example of my email header.

Example1: user save a message, system save record and send email to contact.


To: 
From: 
Subject: RE:[CASE:2] crashing problem 
Message-ID: <SG2PR04MB304551D853DD959B2285A800D6E70@SG2PR04MB3045.apcprd04.prod.outlook.com> 
X-Mailer: PHPMailer 6.0.7 (https://github.com/PHPMailer/PHPMailer) 
Content-Type: multipart/alternative; 
boundary="b1_2KwXqMpKoyI3EUkH1OObSCkErZMoTN9QznSx81PPT4" 
X-MS-Exchange-Organization-ExpirationStartTime: 21 Jun 2019 08:36:19.1783

Example2: customer reply email

References: SG2PR04MB304551D853DD959B2285A800D6E70@SG2PR04MB3045.apcprd04.prod.outlook.com
In-Reply-To: SG2PR04MB304551D853DD959B2285A800D6E70@SG2PR04MB3045.apcprd04.prod.outlook.com
From:
Date: Fri, 21 Jun 2019 16:36:41 +0800
Message-ID: CAK4adFjQEbtzMvmMMBmhMyU33piBNbCdE6vmJtsv6TUhKFVW=Q@mail.gmail.com
Subject: Re: [CASE:2] crashing problem
To:
Content-Type: multipart/alternative;
boundary=“00000000000033389e058bd15d6b”
X-IncomingHeaderCount: 15
Return-Path:
X-MS-Exchange-Organization-ExpirationStartTime: 21 Jun 2019 08:36:52.1065
(UTC)

Hi pgr,

I add code in line 303, in \modules\AOP_Case_Updates\AOP_Case_Updates.php. It was able to send email with “In-Reply-to” and “References” in the header. But, the email still doesn’t group together.

foreach ($emails as $email) {
    $mailer->addAddress($email);
    $mailer->addCustomHeader( 'In-Reply-To', '<SG2PR04MB304514DEF20B595CE47EB05DD6F90@SG2PR04MB3045.apcprd04.prod.outlook.com>' );
    $mailer->addCustomHeader( 'References', '<SG2PR04MB304514DEF20B595CE47EB05DD6F90@SG2PR04MB3045.apcprd04.prod.outlook.com>' );
}

Ok, I think you’re heading in the right direction…

Now, I don’t know enough about how Gmail handles this to help you with more precision… does that answer I linked above give you some clues?

https://webapps.stackexchange.com/questions/965/how-does-gmail-decide-to-thread-email-messages

Maybe the subject line? Or the sender being the same email that received it earlier?

You might be better off looking for help about Gmail in more generic sites like StackOverflow etc. But if you get it working then please come back here since we can try putting this in SuiteCRM core code.

hi pgr,

Only add in_reply_to is enough to group the email (The First email). But there is a minor problem, message_id from apcprd04.prod.outlook.com will not work. The final result looks like the picture I attached. I think is compose issues or other problem.

my concept is:

  1. Create New Case
  2. Auto Send Case Createion Email notification
  3. Store the email message_id
  4. User update the case
  5. Auto Send Contact Case Update Email notification with “In-Reply-To:” + step3 message_id
  6. Continue step 4

Now, I add a new table with a record, but i can’t retrieve the record inside the table.

in \modules\AOP_Case_Updates\AOP_Case_Updates.php, line 311 , after addAddress.

$db = DBManagerFactory::getInstance();
        $query = "SELECT in_reply_to FROM pointer_msgid WHERE subject = '" . $text['subject'] . "' and contact_id_0 = '" . $contactId . "'";
        $in_reply_to = $GLOBALS['db']->query($query, true);
        $mailer->addCustomHeader( 'In-Reply-To', $in_reply_to );

hi pgr

I have done add those code, email is grouping together. I am using the text file in step 1 because I can’t store into the database.

Step1 - modules\AOP_Case_Updates\CaseUpdatesHook.php, line554: after send email

$message_id_1 = $mailer->getMessageID();
$myfile = fopen("messageid.txt","a") or die(" ");
$text = "".PHP_EOL."".$bean->id.":".$email.":".$message_id_1."";
fwrite($myfile,$text);
fclose($myfile);

Step2 - include\SugarPHPMailer.php: add new function

public function getMessageID(){
 $message_id_0 = parent::getLastMessageID();
$GLOBALS['log']->debug("getLastMessageID_0: ".$message_id_0);
return $message_id_0;
}

Step3 - modules\AOP_Case_Updates\AOP_Case_Updates.php, line 306: after foreach email

$myfile = fopen("messageid.txt","r") or die(" ");
while (!feof($myfile)) {
    $data = explode(":",fgets($myfile));
    if($data[0]==$caseId && $data[1]==$email){
        $mailer->addCustomHeader("In-Reply-To",$data[2]);
    }
}
fclose($myfile);

Step1b (Not work) - modules\AOP_Case_Updates\CaseUpdatesHook.php, line554: after send email

$message_id_1 = $mailer->getMessageID();
$pointer = BeanFactory::getBean('Cases', $bean->id);
$pointer->message_id_c = $email.":".$message_id_1;
$pointer->save();

Step3b (Not work) - modules\AOP_Case_Updates\AOP_Case_Updates.php, line 306: after foreach email


        $pointer = BeanFactory::getBean('Cases', $caseId);
        $arr = $pointer->message_id_c;
        $rows = explode(":",$arr);
        $temp = false;
        foreach($rows as $row){
            if($temp){
                $mailer->addCustomHeader("In-Reply-To",$row);
            }
            if($row == $email){
                $temp = true;
            }
        }

Step1b and 3b don’t work because message id doesn’t store into the database.

Sorry, can you explain a little better what is it about the message id that can’t be stored in the database?

I am not fully understanding this code, and your solution, but I would like to.

the message id is retrieved from PHPMailer, getLastMessageId. It is a string example, “SG2PR04MB304514DEF20B595CE47EB05DD6F90@SG2PR04MB3045.apcprd04.prod.outlook.com”. After retrieve then store inside the database.

My code is:
Step1 - store message id into the text file after case creation email is send


$message_id_1 = $mailer->getMessageID(); // retrieve last message id
$myfile = fopen("messageid.txt","a") or die(" "); // open text file
$text = "".PHP_EOL."".$bean->id.":".$email.":".$message_id_1.""; // format the data include "case id:contact email address:message id"
fwrite($myfile,$text); // write the data into the text file
fclose($myfile); // close the text file

Step2 - retrieve message id using PHPMailer function


public function getMessageID(){ // a function to call PHPMailer function, getLastMessageID() which is to retrieve the last generate message id
    $message_id_0 = parent::getLastMessageID(); // then store the retrieved message id to the value
    $GLOBALS['log']->debug("getLastMessageID_0: ".$message_id_0); //log
    return $message_id_0; // return the value, message id
}

Step3 - search matched message id and add to the email before sending the case update email to customer


$myfile = fopen("messageid.txt","r") or die(" "); //open text file
while (!feof($myfile)) { // go though every line of the text file
    $data = explode(":",fgets($myfile)); // separate the line if ":" exist, then store to the array, $data[0] = case id, $data[1] = contact email address, $data[2] = message id
    if($data[0]==$caseId && $data[1]==$email){ // if the case id and contact email if match.
        $mailer->addCustomHeader("In-Reply-To",$data[2]); // add message id to the custom header
    }
}
fclose($myfile); // close text file

In step 1, near your code, there is a command calling “logEmail” function (which is defined in the same file, lime 571).

If you pass the message ID into that function, you could add that to the Email object. The object would have to be changed, and also the database (that object seems to map to the emails_text table), to receive this new field.

(Note that I am considering these changes to be made into core code, as a new feature in SuiteCRM for everybody).

Maybe this would facilitate the rest of the work you need to do in other places?

Are you ably to work on these changes? If not, I suggest you open a new issue on GitHub, with what you learned so far, maybe some other developer can pick it up. Thanks!

Hi pgr,

I still can not save message id into the database even though I try to save at LogEmail function.

Did you succeed in creating the new field in the database?

This would involve adding it to the vardefs (in the core, not as a customization) and then running a QR & R and scrolling to the bottom where it should offer to sync the database.

Hi pgr,

I had done the code.
I had figure out why message id cannot save. It is because the system can’t save the data which start with “<”, special character.
I had created 2 custom field for email module which stores message id and contact email. The custom field for contact email is optional, because I do not know how to retrieve the “to address” in step 3.
This code works for me, but I am not sure if it works for any/all conditions.
Example:
-There is foreach function to add multiple addresses in step3. I am not sure my code is work if there are multiple addresses, since I only store 1 address.
-Maybe my code is insufficient when sending an email with multiple “to address” or “cc”.
I think you can improve my code, so the email function of the suite CRM is more better.

Step1: include\SugarPHPMailer.php: add new function

public function getMessageID(){ // a function to call PHPMailer function, getLastMessageID() which is to retrieve the last generate message id
    $message_id_0 = parent::getLastMessageID(); // then store the retrieved message id to the value
    $GLOBALS['log']->debug("getLastMessageID_0: ".$message_id_0); //log
    return $message_id_0; // return the value, message id
}

Step2: modules\AOP_Case_Updates\CaseUpdatesHook.php, line601 (LogEmail function, after $emailObj->save())

$emailBean = BeanFactory::getBean('Emails',$emailObj->id); //get the email which just now created
$data = array("<",">");//format
$emailBean->msg_id_c = str_replace($data,"",$mailer->getMessageID());//store message id without "<" and ">"
$emailBean->contact_addr_c = $email;//store contact email
$emailBean->save();//save

Step3: modules\AOP_Case_Updates\AOP_Case_Updates.php, line 306: after foreach email

$emailBean = BeanFactory::newBean('Emails');//new Bean
$pointer = $emailBean->retrieve_by_string_fields(
                        array(
                          'parent_id' => $caseId,
                          'contact_addr_c' => $email
                        )
                      );//retrieve the email
$mailer->addCustomHeader("In-Reply-To","<".$pointer->msg_id_c.">");//add pointer

I had created one issue in GitHub #7570.

1 Like

hi pgr,

I still have some question.

  1. how to pass the message_id to the compose email, see pic 1
  2. when I compose and send the email at there, even though there were already selected related to the case, but still do not create a record in the Case Updates Threaded. see pic 2
  3. pic3, there are many disadvantages to reply through the textbox. Like, choosing the recipient and attach some file. I know that there is a subpanel which is the contact subpanel, but it was not so convenient. I would like to choose the recipient (to, cc) and attach some file.

I am not too familiar with Cases and email handling, but I believe that “Case Updates Threaded” and Email threads are two separate mechanisms to have a dialogue around the case. The threaded updates are great for internal discussions, but it seems you want full email capabilities, so you should be working from the Email module.

Sometimes small details are missing (like adding a relationship) and you need to add some custom code…

Hi pgr,

I want the case module to track all my conversation (inbound and outbound). when the customer or user reply, they should able to choose the recipient (to, cc, bcc), include pictures in the body of the message, attach file.

  1. I currently reply the case through the compose email (see attached picture). Am I doing the wrong way?

  2. How can I add the compose email button inside the “Case Updates Threaded” which at the left side of the “save” button? (see second attached picture)

You can “flatten” the subpanel buttons to make them immediately visible, maybe that is enough?

In this directory (create any subdirectories if they don’t exist yet):

custom/Extension/modules/Cases/Ext/Layoutdefs/

create a new file called for example

flat_subpanels.php

Inside it put this:


<?php
$layout_defs['Cases']['subpanel_setup']['activities']['flat'] = 1
?>

Remember users can also re-order their subpanels to bring Activities closer to the top.

The subpanel buttons should appear like this (without the CSS bug): https://suitecrm.com/suitecrm/media/kunena/attachments/34098/suite_buttons_LESS_broken.png

I appear like this.