dev/core#2851 Fix send email task contribution tokens to the processor
authorEileen McNaughton <emcnaughton@wikimedia.org>
Tue, 21 Sep 2021 20:47:51 +0000 (08:47 +1200)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Fri, 24 Sep 2021 19:05:02 +0000 (07:05 +1200)
CRM/Activity/BAO/Activity.php
CRM/Contact/Form/Task/EmailTrait.php
tests/phpunit/CRM/Contribute/Form/Task/EmailTest.php

index f18c933ae2865685ee474ac7ac973d1182394fdd..a3ed1d64e06f66ee87c8ff70e11694eb84f774e7 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 use Civi\Api4\ActivityContact;
+use Civi\Api4\Contribution;
 
 /**
  *
@@ -990,29 +991,29 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
    * @param $html
    * @param string $emailAddress
    *   Use this 'to' email address instead of the default Primary address.
-   * @param int $userID
+   * @param int|null $userID
    *   Use this userID if set.
-   * @param string $from
-   * @param array $attachments
+   * @param string|null $from
+   * @param array|null $attachments
    *   The array of attachments if any.
-   * @param string $cc
+   * @param string|null $cc
    *   Cc recipient.
-   * @param string $bcc
+   * @param string|null $bcc
    *   Bcc recipient.
-   * @param array $contactIds
-   *   Contact ids.
-   * @param string $additionalDetails
+   * @param array|null $contactIds
+   *   unused.
+   * @param string|null $additionalDetails
    *   The additional information of CC and BCC appended to the activity Details.
-   * @param array $contributionIds
-   * @param int $campaignId
-   * @param int $caseId
+   * @param array|null $contributionIds
+   * @param int|null $campaignId
+   * @param int|null $caseId
    *
    * @return array
    *   bool $sent FIXME: this only indicates the status of the last email sent.
    *   array $activityIds The activity ids created, one per "To" recipient.
    *
+   * @throws \API_Exception
    * @throws \CRM_Core_Exception
-   * @throws \CiviCRM_API3_Exception
    */
   public static function sendEmail(
     $contactDetails,
@@ -1036,7 +1037,7 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
       $userID = CRM_Core_Session::getLoggedInContactID();
     }
 
-    list($fromDisplayName, $fromEmail, $fromDoNotEmail) = CRM_Contact_BAO_Contact::getContactDetails($userID);
+    [$fromDisplayName, $fromEmail, $fromDoNotEmail] = CRM_Contact_BAO_Contact::getContactDetails($userID);
     if (!$fromEmail) {
       return [count($contactDetails), 0, count($contactDetails)];
     }
@@ -1044,35 +1045,21 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
       $fromDisplayName = $fromEmail;
     }
 
-    // CRM-4575
-    // token replacement of addressee/email/postal greetings
-    // get the tokens added in subject and message
-    $subjectToken = CRM_Utils_Token::getTokens($subject);
-    $messageToken = CRM_Utils_Token::getTokens($text);
-    $messageToken = array_merge($messageToken, CRM_Utils_Token::getTokens($html));
-    $allTokens = CRM_Utils_Array::crmArrayMerge($messageToken, $subjectToken);
-
     if (!$from) {
       $from = "$fromDisplayName <$fromEmail>";
     }
 
-    $escapeSmarty = FALSE;
-    if (defined('CIVICRM_MAIL_SMARTY') && CIVICRM_MAIL_SMARTY) {
-      $smarty = CRM_Core_Smarty::singleton();
-      $escapeSmarty = TRUE;
-    }
-
     $contributionDetails = [];
     if (!empty($contributionIds)) {
-      $contributionDetails = CRM_Contribute_BAO_Contribution::replaceContributionTokens(
-        $contributionIds,
-        $subject,
-        $subjectToken,
-        $text,
-        $html,
-        $messageToken,
-        $escapeSmarty
-      );
+      $contributionDetails = Contribution::get(FALSE)
+        ->setSelect(['contact_id'])
+        ->addWhere('id', 'IN', $contributionIds)
+        ->execute()
+        // Note that this indexing means that only the last
+        // contribution per contact is resolved to tokens.
+        // this is long-standing functionality, albeit possibly
+        // not thought through.
+        ->indexBy('contact_id');
     }
 
     $sent = $notSent = [];
@@ -1080,13 +1067,12 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
     $activityIds = [];
     $firstActivityCreated = FALSE;
     foreach ($contactDetails as $values) {
+      $tokenContext = $caseId ? ['caseId' => $caseId] : [];
       $contactId = $values['contact_id'];
       $emailAddress = $values['email'];
 
       if (!empty($contributionDetails)) {
-        $subject = $contributionDetails[$contactId]['subject'];
-        $text = $contributionDetails[$contactId]['text'];
-        $html = $contributionDetails[$contactId]['html'];
+        $tokenContext['contributionId'] = $contributionDetails[$contactId]['id'];
       }
 
       $tokenSubject = $subject;
@@ -1099,7 +1085,7 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
           'msg_html' => $tokenHtml,
           'msg_subject' => $tokenSubject,
         ],
-        'tokenContext' => $caseId ? ['caseId' => $caseId] : [],
+        'tokenContext' => $tokenContext,
         'contactId' => $contactId,
         'disableSmarty' => !CRM_Utils_Constant::value('CIVICRM_MAIL_SMARTY'),
       ]);
@@ -1263,7 +1249,7 @@ WHERE entity_id =%1 AND entity_table = %2";
     // get token details for contacts, call only if tokens are used
     $tokenDetails = [];
     if (!empty($returnProperties) || !empty($tokens)) {
-      list($tokenDetails) = CRM_Utils_Token::getTokenDetails($contactIds,
+      [$tokenDetails] = CRM_Utils_Token::getTokenDetails($contactIds,
         $returnProperties,
         NULL, NULL, FALSE,
         $messageToken,
@@ -1430,7 +1416,7 @@ WHERE entity_id =%1 AND entity_table = %2";
     $cc = NULL,
     $bcc = NULL
   ) {
-    list($toDisplayName, $toEmail, $toDoNotEmail) = CRM_Contact_BAO_Contact::getContactDetails($toID);
+    [$toDisplayName, $toEmail, $toDoNotEmail] = CRM_Contact_BAO_Contact::getContactDetails($toID);
     if ($emailAddress) {
       $toEmail = trim($emailAddress);
     }
@@ -1711,7 +1697,7 @@ WHERE      activity.id IN ($activityIds)";
         }
 
         if ($entityObj->owner_membership_id) {
-          list($displayName) = CRM_Contact_BAO_Contact::getDisplayAndImage(CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership', $entityObj->owner_membership_id, 'contact_id'));
+          [$displayName] = CRM_Contact_BAO_Contact::getDisplayAndImage(CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership', $entityObj->owner_membership_id, 'contact_id'));
           $subject .= sprintf(' (by %s)', $displayName);
         }
 
index 00d84dd5e29b7e0c536647ca1653e41906094a8c..ac47eaeed8ee353b77380e2f002848c1515fa89f 100644 (file)
@@ -690,6 +690,11 @@ trait CRM_Contact_Form_Task_EmailTrait {
   /**
    * Prevent submission of deprecated tokens.
    *
+   * Note this rule can be removed after a transition period.
+   * It's mostly to help to ensure users don't get missing tokens
+   * or unexpected output after the 5.43 upgrade until any
+   * old templates have aged out.
+   *
    * @param array $fields
    *
    * @return bool|string[]
@@ -702,6 +707,10 @@ trait CRM_Contact_Form_Task_EmailTrait {
       '{contribution.payment_instrument}' => '{contribution.payment_instrument_id:label}',
       '{contribution.contribution_id}' => '{contribution.id}',
       '{contribution.contribution_source}' => '{contribution.source}',
+      '{contribution.contribution_status}' => '{contribution.contribution_status_id:label}',
+      '{contribution.contribution_cancel_date}' => '{contribution.cancel_date}',
+      '{contribution.type}' => '{contribution.financial_type_id:label}',
+      '{contribution.contribution_page_id}' => '{contribution.contribution_page_id:label}',
     ];
     $tokenErrors = [];
     foreach ($deprecatedTokens as $token => $replacement) {
index f2061638c1413c0ff04f3bbc2f55e282e4e81c29..9ad4974b9c8950a6039ed75615a9637ed84712f4 100644 (file)
@@ -35,8 +35,9 @@ class CRM_Contribute_Form_Task_EmailTest extends CiviUnitTestCase {
   public function testEmailTokens(): void {
     Civi::settings()->set('max_attachments', 0);
     $contact1 = $this->individualCreate();
-    $contact2 = $this->individualCreate();
+    $contact2 = $this->individualCreate(['first_name' => 'Elton']);
     $userID = $this->createLoggedInUser();
+    $mut = new CiviMailUtils($this);
     Civi::settings()->set('allow_mail_from_logged_in_contact', TRUE);
     $this->callAPISuccess('Email', 'create', [
       'contact_id' => $userID,
@@ -44,8 +45,9 @@ class CRM_Contribute_Form_Task_EmailTest extends CiviUnitTestCase {
       'signature_html' => 'Benny, Benny',
       'is_primary' => 1,
     ]);
-    $contribution1 = $this->contributionCreate(['contact_id' => $contact2]);
-    $contribution2 = $this->contributionCreate(['total_amount' => 999, 'contact_id' => $contact1]);
+    $contribution1 = $this->contributionCreate(['contact_id' => $contact2, 'invoice_number' => 'soy']);
+    $contribution2 = $this->contributionCreate(['total_amount' => 999, 'contact_id' => $contact1, 'invoice_number' => 'saucy']);
+    $contribution3 = $this->contributionCreate(['total_amount' => 999, 'contact_id' => $contact1, 'invoice_number' => 'ranch']);
     $form = $this->getFormObject('CRM_Contribute_Form_Task_Email', [
       'cc_id' => '',
       'bcc_id' => '',
@@ -53,19 +55,28 @@ class CRM_Contribute_Form_Task_EmailTest extends CiviUnitTestCase {
         $contact1 . '::teresajensen-nielsen65@spamalot.co.in',
         $contact2 . '::bob@example.com',
       ]),
-      'subject' => '{contact.display_name}',
-      'text_message' => '{contribution.total_amount}',
+      'subject' => '{contact.display_name} {contribution.total_amount}',
+      'text_message' => '{contribution.financial_type_id:label} {contribution.invoice_number}',
       'html_message' => '{domain.name}',
     ], [], [
       'radio_ts' => 'ts_sel',
       'task' => CRM_Core_Task::TASK_EMAIL,
       'mark_x_' . $contribution1 => 1,
       'mark_x_' . $contribution2 => 1,
+      'mark_x_' . $contribution3 => 1,
     ]);
     $form->set('cid', $contact1 . ',' . $contact2);
     $form->buildForm();
     $this->assertEquals('<br/><br/>--Benny, Benny', $form->_defaultValues['html_message']);
     $form->postProcess();
+    $mut->assertSubjects(['Mr. Anthony Anderson II $ 999.00', 'Mr. Elton Anderson II $ 100.00']);
+    $mut->checkAllMailLog([
+      'Subject: Mr. Anthony Anderson II',
+      '$ 999.0',
+      'Default Domain Name',
+      'Donation soy',
+      'Donation ranch',
+    ]);
   }
 
 }