diff --git a/CRM/Activity/BAO/Activity.php b/CRM/Activity/BAO/Activity.php index f18c933ae2865685ee474ac7ac973d1182394fdd..a3ed1d64e06f66ee87c8ff70e11694eb84f774e7 100644 --- a/CRM/Activity/BAO/Activity.php +++ b/CRM/Activity/BAO/Activity.php @@ -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); } diff --git a/CRM/Contact/Form/Task/EmailTrait.php b/CRM/Contact/Form/Task/EmailTrait.php index 00d84dd5e29b7e0c536647ca1653e41906094a8c..ac47eaeed8ee353b77380e2f002848c1515fa89f 100644 --- a/CRM/Contact/Form/Task/EmailTrait.php +++ b/CRM/Contact/Form/Task/EmailTrait.php @@ -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) { diff --git a/tests/phpunit/CRM/Contribute/Form/Task/EmailTest.php b/tests/phpunit/CRM/Contribute/Form/Task/EmailTest.php index f2061638c1413c0ff04f3bbc2f55e282e4e81c29..9ad4974b9c8950a6039ed75615a9637ed84712f4 100644 --- a/tests/phpunit/CRM/Contribute/Form/Task/EmailTest.php +++ b/tests/phpunit/CRM/Contribute/Form/Task/EmailTest.php @@ -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', + ]); } }