Add token support for participant & membership tasks
authorEileen McNaughton <emcnaughton@wikimedia.org>
Wed, 6 Oct 2021 11:01:03 +0000 (00:01 +1300)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Thu, 7 Oct 2021 01:04:55 +0000 (14:04 +1300)
CRM/Case/Form/Task/Email.php
CRM/Contact/Form/Task/EmailTrait.php
CRM/Contact/Form/Task/PDFTrait.php
CRM/Contribute/Form/Task/Email.php
CRM/Core/Form/Task.php
CRM/Event/Form/Task/Email.php
CRM/Member/Form/Task.php
CRM/Member/Form/Task/Email.php

index d96114141aecdc2bfb6fd3e092d0b184d381f1c9..9c72c8e3d116608905554d0c4af00527052cebbe 100644 (file)
@@ -66,4 +66,18 @@ class CRM_Case_Form_Task_Email extends CRM_Case_Form_Task {
     return $subject;
   }
 
+  /**
+   * Get the result rows to email.
+   *
+   * @return array
+   */
+  protected function getRows(): array {
+    // format contact details array to handle multiple emails from same contact
+    $rows = parent::getRows();
+    foreach ($rows as $index => $row) {
+      $rows[$index]['schema']['caseId'] = $this->getCaseID();
+    }
+    return $rows;
+  }
+
 }
index 278b256018fac07772c5d5c37fcc792484d8bb1a..f79b53c40df6e4e0408282ea14022ffcba05447f 100644 (file)
@@ -16,7 +16,6 @@
  */
 
 use Civi\Api4\Email;
-use Civi\Api4\Contribution;
 
 /**
  * This class provides the common functionality for tasks that send emails.
@@ -374,15 +373,8 @@ trait CRM_Contact_Form_Task_EmailTrait {
     $bcc = $this->getEmailString($bccArray);
     $additionalDetails .= empty($bccArray) ? '' : "\nbcc : " . $this->getEmailUrlString($bccArray);
 
-    // format contact details array to handle multiple emails from same contact
-    $formattedContactDetails = [];
-    foreach ($this->getEmails() as $details) {
-      $formattedContactDetails[$details['contact_id'] . '::' . $details['email']] = $details;
-    }
-
     // send the mail
     [$sent, $activityIds] = $this->sendEmail(
-      $formattedContactDetails,
       $formValues['text_message'],
       $formValues['html_message'],
       $from,
@@ -390,7 +382,6 @@ trait CRM_Contact_Form_Task_EmailTrait {
       $cc,
       $bcc,
       $additionalDetails,
-      $this->getContributionIDs(),
       CRM_Utils_Array::value('campaign_id', $formValues),
       $this->getCaseID()
     );
@@ -448,15 +439,6 @@ trait CRM_Contact_Form_Task_EmailTrait {
     }
   }
 
-  /**
-   * List available tokens for this form.
-   *
-   * @return array
-   */
-  public function listTokens(): array {
-    return CRM_Core_SelectValues::contactTokens();
-  }
-
   /**
    * Get the emails from the added element.
    *
@@ -714,8 +696,6 @@ trait CRM_Contact_Form_Task_EmailTrait {
    *
    * Also insert a contact activity in each contacts record.
    *
-   * @param array $contactDetails
-   *   The array of contact details to send the email.
    * @param $text
    * @param $html
    * @param string $from
@@ -727,7 +707,6 @@ trait CRM_Contact_Form_Task_EmailTrait {
    *   Bcc recipient.
    * @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
    *
@@ -737,14 +716,16 @@ trait CRM_Contact_Form_Task_EmailTrait {
    *
    * @throws \API_Exception
    * @throws \CRM_Core_Exception
-   * @throws \Civi\API\Exception\UnauthorizedException
+   * @throws \PEAR_Exception
    * @internal
    *
    * Also insert a contact activity in each contacts record.
    *
+   * @internal
+   *
+   * Also insert a contact activity in each contacts record.
    */
   public function sendEmail(
-    $contactDetails,
     $text,
     $html,
     $from,
@@ -752,46 +733,26 @@ trait CRM_Contact_Form_Task_EmailTrait {
     $cc = NULL,
     $bcc = NULL,
     $additionalDetails = NULL,
-    $contributionIds = NULL,
     $campaignId = NULL,
     $caseId = NULL
   ) {
 
     $userID = CRM_Core_Session::getLoggedInContactID();
 
-    $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] : [];
+    foreach ($this->getRowsForEmails() as $values) {
       $contactId = $values['contact_id'];
       $emailAddress = $values['email'];
-
-      if (!empty($contributionDetails)) {
-        $tokenContext['contributionId'] = $contributionDetails[$contactId]['id'];
-      }
-
       $renderedTemplate = CRM_Core_BAO_MessageTemplate::renderTemplate([
         'messageTemplate' => [
           'msg_text' => $text,
           'msg_html' => $html,
           'msg_subject' => $this->getSubject(),
         ],
-        'tokenContext' => $tokenContext,
+        'tokenContext' => array_merge(['schema' => $this->getTokenSchema()], ($values['schema'] ?? [])),
         'contactId' => $contactId,
         'disableSmarty' => !CRM_Utils_Constant::value('CIVICRM_MAIL_SMARTY'),
       ]);
@@ -990,4 +951,66 @@ trait CRM_Contact_Form_Task_EmailTrait {
     return $url;
   }
 
+  /**
+   * Get the result rows to email.
+   *
+   * @return array
+   *
+   * @throws \API_Exception
+   * @throws \CRM_Core_Exception
+   */
+  protected function getRowsForEmails(): array {
+    $rows = [];
+    foreach ($this->getRows() as $row) {
+      $rows[$row['contact_id']][] = $row;
+    }
+    // format contact details array to handle multiple emails from same contact
+    $formattedContactDetails = [];
+    foreach ($this->getEmails() as $details) {
+      $contactID = $details['contact_id'];
+      $index = $contactID . '::' . $details['email'];
+      if (!isset($rows[$contactID])) {
+        $formattedContactDetails[$index] = $details;
+        continue;
+      }
+      if ($this->isGroupByContact()) {
+        foreach ($rows[$contactID] as $rowDetail) {
+          $details['schema'] = $rowDetail['schema'] ?? [];
+        }
+        $formattedContactDetails[$index] = $details;
+      }
+      else {
+        foreach ($rows[$contactID] as $key => $rowDetail) {
+          $index .= '_' . $key;
+          $formattedContactDetails[$index] = $details;
+          $formattedContactDetails[$index]['schema'] = $rowDetail['schema'] ?? [];
+        }
+      }
+
+    }
+    return $formattedContactDetails;
+  }
+
+  /**
+   * Only send one email per contact.
+   *
+   * This has historically been done for contributions & makes sense if
+   * no entity specific tokens are in use.
+   *
+   * @return bool
+   */
+  protected function isGroupByContact(): bool {
+    return TRUE;
+  }
+
+  /**
+   * Get the tokens in the submitted message.
+   *
+   * @return array
+   * @throws \CRM_Core_Exception
+   */
+  protected function getMessageTokens(): array {
+    return CRM_Utils_Token::getTokens($this->getSubject() . $this->getSubmittedValue('html_message') . $this->getSubmittedValue('text_message'));
+  }
+
 }
index dae3544153d7eb96833d1e7a25bd1eb55de38642..8a3146c8d8b949053926096e6e39810bbcb40101 100644 (file)
@@ -241,7 +241,7 @@ trait CRM_Contact_Form_Task_PDFTrait {
       $tokenHtml = CRM_Core_BAO_MessageTemplate::renderTemplate([
         'contactId' => $row['contact_id'],
         'messageTemplate' => ['msg_html' => $html_message],
-        'tokenContext' => ['schema' => $row['schema']],
+        'tokenContext' => array_merge(['schema' => $this->getTokenSchema()], ($row['schema'] ?? [])),
         'disableSmarty' => (!defined('CIVICRM_MAIL_SMARTY') || !CIVICRM_MAIL_SMARTY),
       ])['html'];
 
index 7fb87e4c97f78ebd041c0e5320754e6bfe46b521..da4b638b3620fdd5b3ab39f2cd3792618351abad 100644 (file)
@@ -15,6 +15,8 @@
  * @copyright CiviCRM LLC https://civicrm.org/licensing
  */
 
+use Civi\Api4\Contribution;
+
 /**
  * This class provides the functionality to email a group of contacts.
  */
@@ -32,4 +34,35 @@ class CRM_Contribute_Form_Task_Email extends CRM_Contribute_Form_Task {
     return $this->getIDs();
   }
 
+  /**
+   * Get the result rows to email.
+   *
+   * @return array
+   *
+   * @throws \API_Exception
+   * @throws \CRM_Core_Exception
+   */
+  protected function getRows(): array {
+    $contributionDetails = Contribution::get(FALSE)
+      ->setSelect(['contact_id', 'id'])
+      ->addWhere('id', 'IN', $this->getContributionIDs())
+      ->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');
+
+    // format contact details array to handle multiple emails from same contact
+    $formattedContactDetails = [];
+    foreach ($this->getEmails() as $details) {
+      $formattedContactDetails[$details['contact_id'] . '::' . $details['email']] = $details;
+      if (!empty($contributionDetails[$details['contact_id']])) {
+        $formattedContactDetails[$details['contact_id'] . '::' . $details['email']]['schema'] = ['contributionId' => $contributionDetails[$details['contact_id']]['id']];
+      }
+
+    }
+    return $formattedContactDetails;
+  }
+
 }
index 5e8501a6cee566f49b5a2bfdc08b18b98fc2fbbc..3c134e608cc1fc27eb1c3b59870dca686c1abe9f 100644 (file)
@@ -75,6 +75,18 @@ abstract class CRM_Core_Form_Task extends CRM_Core_Form {
    */
   public static $entityShortname = NULL;
 
+
+  /**
+   * Rows to act on.
+   *
+   * e.g
+   *  [
+   *    ['contact_id' => 4, 'participant_id' => 6, 'schema' => ['contactId' => 5, 'participantId' => 6],
+   *  ]
+   * @var array
+   */
+  protected $rows = [];
+
   /**
    * Set where the browser should be directed to next.
    *
@@ -367,7 +379,7 @@ SELECT contact_id
   protected function getRows(): array {
     $rows = [];
     foreach ($this->getContactIDs() as $contactID) {
-      $rows[] = ['schema' => ['contactId' => $contactID]];
+      $rows[] = ['contact_id' => $contactID, 'schema' => ['contactId' => $contactID]];
     }
     return $rows;
   }
index cd0270b3764bd661b2121f02695f271322cec0ab..85f1af9f80bf63466eb0a7b3bb6ab0ff0f518ebe 100644 (file)
 class CRM_Event_Form_Task_Email extends CRM_Event_Form_Task {
   use CRM_Contact_Form_Task_EmailTrait;
 
+  /**
+   * Only send one email per contact.
+   *
+   * This has historically been done for contributions & makes sense if
+   * no entity specific tokens are in use.
+   *
+   * @return bool
+   * @throws \CRM_Core_Exception
+   */
+  protected function isGroupByContact(): bool {
+    return !empty($this->getMessageTokens()['participant']) || !empty($this->getMessageTokens()['event']);
+  }
+
 }
index 9dd37aa1b90f451d44d3e308434e576f89185cfb..756355c1e949a5944a9779dd6e43a6555bd88cee 100644 (file)
@@ -15,6 +15,8 @@
  * @copyright CiviCRM LLC https://civicrm.org/licensing
  */
 
+use Civi\Api4\Membership;
+
 /**
  * Class for member form task actions.
  * FIXME: This needs refactoring to properly inherit from CRM_Core_Form_Task and share more functions.
@@ -95,6 +97,38 @@ class CRM_Member_Form_Task extends CRM_Core_Form_Task {
     );
   }
 
+  /**
+   * @return array
+   */
+  protected function getIDS() {
+    return $this->_memberIds;
+  }
+
+  /**
+   * Get the rows form the search, keyed to make the token processor happy.
+   *
+   * @throws \API_Exception
+   */
+  protected function getRows(): array {
+    if (empty($this->rows)) {
+      // checkPermissions set to false - in case form is bypassing in some way.
+      $memberships = Membership::get(FALSE)
+        ->addWhere('id', 'IN', $this->getIDs())
+        ->setSelect(['id', 'contact_id'])->execute();
+      foreach ($memberships as $membership) {
+        $this->rows[] = [
+          'contact_id' => $membership['contact_id'],
+          'membership_id' => $membership['id'],
+          'schema' => [
+            'contactId' => $membership['contact_id'],
+            'membershipId' => $membership['id'],
+          ],
+        ];
+      }
+    }
+    return $this->rows;
+  }
+
   /**
    * Get the token processor schema required to list any tokens for this task.
    *
index 5e0e6d4baa61941ac9092f1cad8a107fe7d10e07..9f19d0fe7ee6d8a57f0ca0f3065a39acd53a26f8 100644 (file)
 class CRM_Member_Form_Task_Email extends CRM_Member_Form_Task {
   use CRM_Contact_Form_Task_EmailTrait;
 
+  /**
+   * Only send one email per contact.
+   *
+   * This has historically been done for contributions & makes sense if
+   * no entity specific tokens are in use.
+   *
+   * @return bool
+   * @throws \CRM_Core_Exception
+   */
+  protected function isGroupByContact(): bool {
+    return !empty($this->getMessageTokens()['membership']);
+  }
+
 }