Merge pull request #21554 from eileenmcnaughton/loop
[civicrm-core.git] / CRM / Contact / Form / Task / EmailTrait.php
index 31ec4f80315c3631239ef494b4a54c3a061881cb..db6af3198c38e170fbe36c93db02bbf4a96d6b4e 100644 (file)
@@ -37,6 +37,13 @@ trait CRM_Contact_Form_Task_EmailTrait {
    */
   public $_templates;
 
+  /**
+   * Email addresses to send to.
+   *
+   * @var array
+   */
+  protected $emails = [];
+
   /**
    * Store "to" contact details.
    * @var array
@@ -55,19 +62,6 @@ trait CRM_Contact_Form_Task_EmailTrait {
    */
   public $_toContactIds = [];
 
-  /**
-   * Store only "cc" contact ids.
-   * @var array
-   */
-  public $_ccContactIds = [];
-
-  /**
-   * Store only "bcc" contact ids.
-   *
-   * @var array
-   */
-  public $_bccContactIds = [];
-
   /**
    * Is the form being loaded from a search action.
    *
@@ -118,13 +112,14 @@ trait CRM_Contact_Form_Task_EmailTrait {
    * Call trait preProcess function.
    *
    * This function exists as a transitional arrangement so classes overriding
-   * preProcess can still call it. Ideally it will be melded into preProcess later.
+   * preProcess can still call it. Ideally it will be melded into preProcess
+   * later.
    *
-   * @throws \CiviCRM_API3_Exception
    * @throws \CRM_Core_Exception
+   * @throws \API_Exception
    */
   protected function traitPreProcess() {
-    CRM_Contact_Form_Task_EmailCommon::preProcessFromAddress($this);
+    $this->preProcessFromAddress();
     if ($this->isSearchContext()) {
       // Currently only the contact email form is callable outside search context.
       parent::preProcess();
@@ -136,6 +131,22 @@ trait CRM_Contact_Form_Task_EmailTrait {
     }
   }
 
+  /**
+   * Pre Process Form Addresses to be used in Quickform
+   *
+   * @throws \API_Exception
+   * @throws \CRM_Core_Exception
+   */
+  protected function preProcessFromAddress(): void {
+    $form = $this;
+    $form->_emails = [];
+
+    // @TODO remove these line and to it somewhere more appropriate. Currently some classes (e.g Case
+    // are having to re-write contactIds afterwards due to this inappropriate variable setting
+    // If we don't have any contact IDs, use the logged in contact ID
+    $form->_contactIds = $form->_contactIds ?: [CRM_Core_Session::getLoggedInContactID()];
+  }
+
   /**
    * Build the form object.
    *
@@ -186,10 +197,8 @@ trait CRM_Contact_Form_Task_EmailTrait {
     if ($to->getValue()) {
       foreach ($this->getEmails($to) as $value) {
         $contactId = $value['contact_id'];
-        $email = $value['email'];
         if ($contactId) {
           $this->_contactIds[] = $this->_toContactIds[] = $contactId;
-          $this->_toContactEmails[] = $email;
           $this->_allContactIds[] = $contactId;
         }
       }
@@ -247,7 +256,7 @@ trait CRM_Contact_Form_Task_EmailTrait {
 
     $this->add('text', 'subject', ts('Subject'), ['size' => 50, 'maxlength' => 254], TRUE);
 
-    $this->add('select', 'from_email_address', ts('From'), $this->_fromEmails, TRUE);
+    $this->add('select', 'from_email_address', ts('From'), $this->getFromEmails(), TRUE);
 
     CRM_Mailing_BAO_Mailing::commonCompose($this);
 
@@ -256,12 +265,12 @@ trait CRM_Contact_Form_Task_EmailTrait {
 
     if ($this->_single) {
       // also fix the user context stack
-      if ($this->_caseId) {
+      if ($this->getCaseID()) {
         $ccid = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseContact', $this->_caseId,
           'contact_id', 'case_id'
         );
         $url = CRM_Utils_System::url('civicrm/contact/view/case',
-          "&reset=1&action=view&cid={$ccid}&id={$this->_caseId}"
+          "&reset=1&action=view&cid={$ccid}&id=" . $this->getCaseID()
         );
       }
       elseif ($this->_context) {
@@ -275,11 +284,8 @@ trait CRM_Contact_Form_Task_EmailTrait {
 
       $session = CRM_Core_Session::singleton();
       $session->replaceUserContext($url);
-      $this->addDefaultButtons(ts('Send Email'), 'upload', 'cancel');
-    }
-    else {
-      $this->addDefaultButtons(ts('Send Email'), 'upload');
     }
+    $this->addDefaultButtons(ts('Send Email'), 'upload', 'cancel');
 
     $fields = [
       'followup_assignee_contact_id' => [
@@ -330,9 +336,31 @@ trait CRM_Contact_Form_Task_EmailTrait {
     CRM_Campaign_BAO_Campaign::addCampaign($this);
 
     $this->addFormRule([__CLASS__, 'saveTemplateFormRule'], $this);
+    $this->addFormRule([__CLASS__, 'deprecatedTokensFormRule'], $this);
     CRM_Core_Resources::singleton()->addScriptFile('civicrm', 'templates/CRM/Contact/Form/Task/EmailCommon.js', 0, 'html-header');
   }
 
+  /**
+   * Set relevant default values.
+   *
+   * @return array
+   *
+   * @throws \API_Exception
+   * @throws \CRM_Core_Exception
+   */
+  public function setDefaultValues(): array {
+    $defaults = parent::setDefaultValues();
+    $fromEmails = $this->getFromEmails();
+    if (is_numeric(key($fromEmails))) {
+      $emailID = (int) key($fromEmails);
+      $defaults = CRM_Core_BAO_Email::getEmailSignatureDefaults($emailID);
+    }
+    if (!Civi::settings()->get('allow_mail_from_logged_in_contact')) {
+      $defaults['from_email_address'] = current(CRM_Core_BAO_Domain::getNameAndEmail(FALSE, TRUE));
+    }
+    return $defaults;
+  }
+
   /**
    * Process the form after the input has been submitted and validated.
    *
@@ -343,8 +371,6 @@ trait CRM_Contact_Form_Task_EmailTrait {
    */
   public function postProcess() {
     $this->bounceIfSimpleMailLimitExceeded(count($this->_contactIds));
-
-    // check and ensure that
     $formValues = $this->controller->exportValues($this->getName());
     $this->submit($formValues);
   }
@@ -355,7 +381,7 @@ trait CRM_Contact_Form_Task_EmailTrait {
    * @param int $count
    *  The number of emails the user is attempting to send
    */
-  protected function bounceIfSimpleMailLimitExceeded($count) {
+  protected function bounceIfSimpleMailLimitExceeded($count): void {
     $limit = Civi::settings()->get('simple_mail_limit');
     if ($count > $limit) {
       CRM_Core_Error::statusBounce(ts('Please do not use this task to send a lot of emails (greater than %1). Many countries have legal requirements when sending bulk emails and the CiviMail framework has opt out functionality and domain tokens to help meet these.',
@@ -376,7 +402,7 @@ trait CRM_Contact_Form_Task_EmailTrait {
    * @throws \Civi\API\Exception\UnauthorizedException
    * @throws \API_Exception
    */
-  public function submit($formValues) {
+  public function submit($formValues): void {
     $this->saveMessageTemplate($formValues);
 
     $from = $formValues['from_email_address'] ?? NULL;
@@ -401,7 +427,7 @@ trait CRM_Contact_Form_Task_EmailTrait {
       if (!isset($this->_contactDetails[$contactId])) {
         continue;
       }
-      $email = $this->_toContactEmails[$key];
+      $email = $this->getEmail($key);
       // prevent duplicate emails if same email address is selected CRM-4067
       // we should allow same emails for different contacts
       $details = $this->_contactDetails[$contactId];
@@ -411,7 +437,7 @@ trait CRM_Contact_Form_Task_EmailTrait {
     }
 
     // send the mail
-    list($sent, $activityIds) = CRM_Activity_BAO_Activity::sendEmail(
+    [$sent, $activityIds] = CRM_Activity_BAO_Activity::sendEmail(
       $formattedContactDetails,
       $this->getSubject($formValues['subject']),
       $formValues['text_message'],
@@ -424,9 +450,9 @@ trait CRM_Contact_Form_Task_EmailTrait {
       $bcc,
       array_keys($this->_toContactDetails),
       $additionalDetails,
-      $this->getVar('_contributionIds') ?? [],
+      $this->getContributionIDs(),
       CRM_Utils_Array::value('campaign_id', $formValues),
-      $this->getVar('_caseId')
+      $this->getCaseID()
     );
 
     if ($sent) {
@@ -590,11 +616,12 @@ trait CRM_Contact_Form_Task_EmailTrait {
    * @param string $subject
    *
    * @return string
+   * @throws \CRM_Core_Exception
    */
   protected function getSubject(string $subject):string {
     // CRM-5916: prepend case id hash to CiviCase-originating emails’ subjects
-    if (isset($this->_caseId) && is_numeric($this->_caseId)) {
-      $hash = substr(sha1(CIVICRM_SITE_KEY . $this->_caseId), 0, 7);
+    if ($this->getCaseID()) {
+      $hash = substr(sha1(CIVICRM_SITE_KEY . $this->getCaseID()), 0, 7);
       $subject = "[case #$hash] $subject";
     }
     return $subject;
@@ -660,4 +687,79 @@ trait CRM_Contact_Form_Task_EmailTrait {
     return empty($errors) ? TRUE : $errors;
   }
 
+  /**
+   * Prevent submission of deprecated tokens.
+   *
+   * @param array $fields
+   *
+   * @return bool|string[]
+   */
+  public static function deprecatedTokensFormRule(array $fields) {
+    $deprecatedTokens = [
+      '{case.status_id}' => '{case.status_id:label}',
+      '{case.case_type_id}' => '{case.case_type_id:label}',
+    ];
+    $tokenErrors = [];
+    foreach ($deprecatedTokens as $token => $replacement) {
+      if (strpos($fields['html_message'], $token) !== FALSE) {
+        $tokenErrors[] = ts('Token %1 is no longer supported - use %2 instead', [$token, $replacement]);
+      }
+    }
+    return empty($tokenErrors) ? TRUE : ['html_message' => implode('<br>', $tokenErrors)];
+  }
+
+  /**
+   * Get selected contribution IDs.
+   *
+   * @return array
+   */
+  protected function getContributionIDs(): array {
+    return [];
+  }
+
+  /**
+   * Get case ID - if any.
+   *
+   * @return int|null
+   *
+   * @throws \CRM_Core_Exception
+   */
+  protected function getCaseID(): ?int {
+    $caseID = CRM_Utils_Request::retrieve('caseid', 'String', $this);
+    if ($caseID) {
+      return (int) $caseID;
+    }
+    return NULL;
+  }
+
+  /**
+   * @return array
+   */
+  protected function getFromEmails(): array {
+    $fromEmailValues = CRM_Core_BAO_Email::getFromEmail();
+
+    if (empty($fromEmailValues)) {
+      CRM_Core_Error::statusBounce(ts('Your user record does not have a valid email address and no from addresses have been configured.'));
+    }
+    return $fromEmailValues;
+  }
+
+  /**
+   * Get the relevant emails.
+   *
+   * @param int $index
+   *
+   * @return string
+   */
+  protected function getEmail(int $index): string {
+    if (empty($this->emails)) {
+      $toEmails = explode(',', $this->getSubmittedValue('to'));
+      foreach ($toEmails as $value) {
+        $parts = explode('::', $value);
+        $this->emails[] = $parts[1];
+      }
+    }
+    return $this->emails[$index];
+  }
+
 }