From 7e27588cdcfc1f9ff03eb978f3f73cde1ed0ca5a Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Sat, 25 Sep 2021 09:13:14 +1200 Subject: [PATCH] Return the sendEmail function to it's owner This sendEmail function is only called from one place in core and it is not 'generally useful' having an awful parameter set. This PR moves it back to the class that 'owns' it - which will allow us to undo all the work of building up that parameter set and make it possible to support tokens for other entities than those already mangled in. I would normally add a noisy deprecation notice once a function becomes unused in core but since that has been done to the pdf task this release I've left this deprecation a bit quieter for now. Note that I cleaned up the tokens handled here before deprecating so we could get rid of those calls fully Under the OO structure it becomes easier to add the missing token options - membership & participant - but the business of 'one email per person, & just grab the tokens from the last entity' is messing with my head a bit. That's the next bit.... --- CRM/Activity/BAO/Activity.php | 12 +- CRM/Contact/Form/Task/EmailTrait.php | 157 +++++++++++++++++- CRM/Core/BAO/File.php | 4 +- .../phpunit/CRM/Activity/BAO/ActivityTest.php | 21 ++- 4 files changed, 183 insertions(+), 11 deletions(-) diff --git a/CRM/Activity/BAO/Activity.php b/CRM/Activity/BAO/Activity.php index a3ed1d64e0..f94545a0ba 100644 --- a/CRM/Activity/BAO/Activity.php +++ b/CRM/Activity/BAO/Activity.php @@ -979,7 +979,7 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity { } /** - * Send the message to all the contacts. + * DO NOT USE THIS FUNCTION - DEPRECATED. * * Also insert a contact activity in each contacts record. * @@ -1012,6 +1012,8 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity { * bool $sent FIXME: this only indicates the status of the last email sent. * array $activityIds The activity ids created, one per "To" recipient. * + * @deprecated + * * @throws \API_Exception * @throws \CRM_Core_Exception */ @@ -1032,6 +1034,10 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity { $campaignId = NULL, $caseId = NULL ) { + // @todo add noisy deprecation. + // This function is no longer called from core. + // I just left off the noisy deprecation for now as there + // are already a lot of other noisy deprecation points in 5.43. // get the contact details of logged in contact, which we set as from email if ($userID == NULL) { $userID = CRM_Core_Session::getLoggedInContactID(); @@ -1142,10 +1148,12 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity { * @param array $attachments * Attachments. * + * @internal + * * @return array * Array of attachment key versus file Id. */ - private static function getAttachmentFileIds($activityID, $attachments) { + public static function getAttachmentFileIds($activityID, $attachments) { $queryParams = [1 => [$activityID, 'Positive'], 2 => [CRM_Activity_DAO_Activity::getTableName(), 'String']]; $query = "SELECT file_id, uri FROM civicrm_entity_file INNER JOIN civicrm_file ON civicrm_entity_file.file_id = civicrm_file.id WHERE entity_id =%1 AND entity_table = %2"; diff --git a/CRM/Contact/Form/Task/EmailTrait.php b/CRM/Contact/Form/Task/EmailTrait.php index ac47eaeed8..d9530c0c5c 100644 --- a/CRM/Contact/Form/Task/EmailTrait.php +++ b/CRM/Contact/Form/Task/EmailTrait.php @@ -16,6 +16,7 @@ */ use Civi\Api4\Email; +use Civi\Api4\Contribution; /** * This class provides the common functionality for tasks that send emails. @@ -437,7 +438,7 @@ trait CRM_Contact_Form_Task_EmailTrait { } // send the mail - [$sent, $activityIds] = CRM_Activity_BAO_Activity::sendEmail( + [$sent, $activityIds] = $this->sendEmail( $formattedContactDetails, $this->getSubject($formValues['subject']), $formValues['text_message'], @@ -775,4 +776,158 @@ trait CRM_Contact_Form_Task_EmailTrait { return $this->emails[$index]; } + /** + * Send the message to all the contacts. + * + * Do not use this function outside of core tested code. It will change. + * + * @internal + * + * Also insert a contact activity in each contacts record. + * + * @param array $contactDetails + * The array of contact details to send the email. + * @param string $subject + * The subject of the message. + * @param $text + * @param $html + * @param string $emailAddress + * Use this 'to' email address instead of the default Primary address. + * @param int|null $userID + * Use this userID if set. + * @param string|null $from + * @param array|null $attachments + * The array of attachments if any. + * @param string|null $cc + * Cc recipient. + * @param string|null $bcc + * Bcc recipient. + * @param array|null $contactIds + * unused. + * @param string|null $additionalDetails + * The additional information of CC and BCC appended to the activity Details. + * @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 + */ + public function sendEmail( + $contactDetails, + $subject, + $text, + $html, + $emailAddress, + $userID = NULL, + $from = NULL, + $attachments = NULL, + $cc = NULL, + $bcc = NULL, + $contactIds = NULL, + $additionalDetails = NULL, + $contributionIds = NULL, + $campaignId = NULL, + $caseId = NULL + ) { + // get the contact details of logged in contact, which we set as from email + if ($userID == NULL) { + $userID = CRM_Core_Session::getLoggedInContactID(); + } + + [$fromDisplayName, $fromEmail, $fromDoNotEmail] = CRM_Contact_BAO_Contact::getContactDetails($userID); + if (!$fromEmail) { + return [count($contactDetails), 0, count($contactDetails)]; + } + if (!trim($fromDisplayName)) { + $fromDisplayName = $fromEmail; + } + + if (!$from) { + $from = "$fromDisplayName <$fromEmail>"; + } + + $contributionDetails = []; + if (!empty($contributionIds)) { + $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 = []; + $attachmentFileIds = []; + $activityIds = []; + $firstActivityCreated = FALSE; + foreach ($contactDetails as $values) { + $tokenContext = $caseId ? ['caseId' => $caseId] : []; + $contactId = $values['contact_id']; + $emailAddress = $values['email']; + + if (!empty($contributionDetails)) { + $tokenContext['contributionId'] = $contributionDetails[$contactId]['id']; + } + + $tokenSubject = $subject; + $tokenText = in_array($values['preferred_mail_format'], ['Both', 'Text'], TRUE) ? $text : ''; + $tokenHtml = in_array($values['preferred_mail_format'], ['Both', 'HTML'], TRUE) ? $html : ''; + + $renderedTemplate = CRM_Core_BAO_MessageTemplate::renderTemplate([ + 'messageTemplate' => [ + 'msg_text' => $tokenText, + 'msg_html' => $tokenHtml, + 'msg_subject' => $tokenSubject, + ], + 'tokenContext' => $tokenContext, + 'contactId' => $contactId, + 'disableSmarty' => !CRM_Utils_Constant::value('CIVICRM_MAIL_SMARTY'), + ]); + + $sent = FALSE; + // To minimize storage requirements, only one copy of any file attachments uploaded to CiviCRM is kept, + // even when multiple contacts will receive separate emails from CiviCRM. + if (!empty($attachmentFileIds)) { + $attachments = array_replace_recursive($attachments, $attachmentFileIds); + } + + // Create email activity. + $activityID = CRM_Activity_BAO_Activity::createEmailActivity($userID, $renderedTemplate['subject'], $renderedTemplate['html'], $renderedTemplate['text'], $additionalDetails, $campaignId, $attachments, $caseId); + $activityIds[] = $activityID; + + if ($firstActivityCreated == FALSE && !empty($attachments)) { + $attachmentFileIds = CRM_Activity_BAO_Activity::getAttachmentFileIds($activityID, $attachments); + $firstActivityCreated = TRUE; + } + + if (CRM_Activity_BAO_Activity::sendMessage( + $from, + $userID, + $contactId, + $renderedTemplate['subject'], + $renderedTemplate['text'], + $renderedTemplate['html'], + $emailAddress, + $activityID, + // get the set of attachments from where they are stored + CRM_Core_BAO_File::getEntityFile('civicrm_activity', $activityID), + $cc, + $bcc + ) + ) { + $sent = TRUE; + } + } + + return [$sent, $activityIds]; + } + } diff --git a/CRM/Core/BAO/File.php b/CRM/Core/BAO/File.php index c2de586197..a420f7ac2b 100644 --- a/CRM/Core/BAO/File.php +++ b/CRM/Core/BAO/File.php @@ -570,8 +570,8 @@ AND CEF.entity_id = %2"; CRM_Utils_File::formatFile($formValues, $attachName, $extraParams); - // set the formatted attachment attributes to $params, later used by - // CRM_Activity_BAO_Activity::sendEmail(...) to send mail with desired attachments + // set the formatted attachment attributes to $params, later used + // to send mail with desired attachments if (!empty($formValues[$attachName])) { $params[$attachName] = $formValues[$attachName]; } diff --git a/tests/phpunit/CRM/Activity/BAO/ActivityTest.php b/tests/phpunit/CRM/Activity/BAO/ActivityTest.php index ab631b0c18..b6619b9eb9 100644 --- a/tests/phpunit/CRM/Activity/BAO/ActivityTest.php +++ b/tests/phpunit/CRM/Activity/BAO/ActivityTest.php @@ -1261,8 +1261,10 @@ class CRM_Activity_BAO_ActivityTest extends CiviUnitTestCase { $text = __FUNCTION__ . ' text {contact.display_name} {case.case_type_id:label}'; $userID = $loggedInUser; + /* @var CRM_Contact_Form_Task_Email $form */ + $form = $this->getFormObject('CRM_Contact_Form_Task_Email'); $mut = new CiviMailUtils($this, TRUE); - [$sent, $activity_ids] = CRM_Activity_BAO_Activity::sendEmail( + [$sent, $activity_ids] = $form->sendEmail( $contactDetails, $subject, $text, @@ -1375,9 +1377,10 @@ class CRM_Activity_BAO_ActivityTest extends CiviUnitTestCase { $subject = __FUNCTION__ . ' subject'; $html = __FUNCTION__ . ' html'; $text = __FUNCTION__ . ' text'; - + /* @var CRM_Contact_Form_Task_Email $form */ + $form = $this->getFormObject('CRM_Contact_Form_Task_Email'); $mut = new CiviMailUtils($this, TRUE); - [$sent, $activity_ids] = CRM_Activity_BAO_Activity::sendEmail( + [$sent, $activity_ids] = $form->sendEmail( $contactDetails, $subject, $text, @@ -1441,8 +1444,10 @@ $text $html = __FUNCTION__ . ' html'; $text = __FUNCTION__ . ' text'; $userID = $loggedInUser; + /* @var CRM_Contact_Form_Task_Email $form */ + $form = $this->getFormObject('CRM_Contact_Form_Task_Email'); - [$sent, $activity_ids] = $email_result = CRM_Activity_BAO_Activity::sendEmail( + [$sent, $activity_ids] = $email_result = $form->sendEmail( $contactDetails, $subject, $text, @@ -1701,7 +1706,9 @@ $text $text = __FUNCTION__ . ' text'; $mut = new CiviMailUtils($this, TRUE); - [$sent, $activity_ids] = $email_result = CRM_Activity_BAO_Activity::sendEmail( + /* @var CRM_Contact_Form_Task_Email $form */ + $form = $this->getFormObject('CRM_Contact_Form_Task_Email'); + [$sent, $activity_ids] = $form->sendEmail( $contact['values'], $subject, $text, @@ -1750,7 +1757,9 @@ $text $text = __FUNCTION__ . ' text' . '{contact.display_name}'; $userID = $loggedInUser; - CRM_Activity_BAO_Activity::sendEmail( + /* @var CRM_Contact_Form_Task_Email $form */ + $form = $this->getFormObject('CRM_Contact_Form_Task_Email'); + $form->sendEmail( $contact['values'], $subject, $text, -- 2.25.1