Remove deprecated code lines
[civicrm-core.git] / CRM / Core / BAO / MessageTemplate.php
index 0025b80e5472d5cdaa2119b16596a302dd00743d..802a3f822fdce8db4fd66a4a902b15117d4cdc35 100644 (file)
@@ -15,7 +15,9 @@
  * @copyright CiviCRM LLC https://civicrm.org/licensing
  */
 
+use Civi\Api4\Email;
 use Civi\Api4\MessageTemplate;
+use Civi\WorkflowMessage\WorkflowMessage;
 
 require_once 'Mail/mime.php';
 
@@ -211,127 +213,6 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate {
     return $msgTpls;
   }
 
-  /**
-   * @param int $contactId
-   * @param $email
-   * @param int $messageTemplateID
-   * @param $from
-   *
-   * @return bool|NULL
-   * @throws \CRM_Core_Exception
-   */
-  public static function sendReminder($contactId, $email, $messageTemplateID, $from) {
-    CRM_Core_Error::deprecatedWarning('CRM_Core_BAO_MessageTemplate::sendReminder is deprecated and will be removed in a future version of CiviCRM');
-
-    $messageTemplates = new CRM_Core_DAO_MessageTemplate();
-    $messageTemplates->id = $messageTemplateID;
-
-    $domain = CRM_Core_BAO_Domain::getDomain();
-    $result = NULL;
-    $hookTokens = [];
-
-    if ($messageTemplates->find(TRUE)) {
-      $body_text = $messageTemplates->msg_text;
-      $body_html = $messageTemplates->msg_html;
-      $body_subject = $messageTemplates->msg_subject;
-      if (!$body_text) {
-        $body_text = CRM_Utils_String::htmlToText($body_html);
-      }
-
-      $params = [['contact_id', '=', $contactId, 0, 0]];
-      [$contact] = CRM_Contact_BAO_Query::apiQuery($params);
-
-      //CRM-4524
-      $contact = reset($contact);
-
-      if (!$contact || is_a($contact, 'CRM_Core_Error')) {
-        return NULL;
-      }
-
-      //CRM-5734
-
-      // get tokens to be replaced
-      $tokens = array_merge(CRM_Utils_Token::getTokens($body_text),
-        CRM_Utils_Token::getTokens($body_html),
-        CRM_Utils_Token::getTokens($body_subject));
-
-      // get replacement text for these tokens
-      $returnProperties = ["preferred_mail_format" => 1];
-      if (isset($tokens['contact'])) {
-        foreach ($tokens['contact'] as $key => $value) {
-          $returnProperties[$value] = 1;
-        }
-      }
-      [$details] = CRM_Utils_Token::getTokenDetails([$contactId],
-        $returnProperties,
-        NULL, NULL, FALSE,
-        $tokens,
-        'CRM_Core_BAO_MessageTemplate');
-      $contact = reset($details);
-
-      // call token hook
-      $hookTokens = [];
-      CRM_Utils_Hook::tokens($hookTokens);
-      $categories = array_keys($hookTokens);
-
-      // do replacements in text and html body
-      $type = ['html', 'text'];
-      foreach ($type as $key => $value) {
-        $bodyType = "body_{$value}";
-        if ($$bodyType) {
-          CRM_Utils_Token::replaceGreetingTokens($$bodyType, NULL, $contact['contact_id']);
-          $$bodyType = CRM_Utils_Token::replaceDomainTokens($$bodyType, $domain, TRUE, $tokens, TRUE);
-          $$bodyType = CRM_Utils_Token::replaceContactTokens($$bodyType, $contact, FALSE, $tokens, FALSE, TRUE);
-          $$bodyType = CRM_Utils_Token::replaceComponentTokens($$bodyType, $contact, $tokens, TRUE);
-          $$bodyType = CRM_Utils_Token::replaceHookTokens($$bodyType, $contact, $categories, TRUE);
-        }
-      }
-      $html = $body_html;
-      $text = $body_text;
-
-      $smarty = CRM_Core_Smarty::singleton();
-      foreach ([
-        'text',
-        'html',
-      ] as $elem) {
-        $$elem = $smarty->fetch("string:{$$elem}");
-      }
-
-      // do replacements in message subject
-      $messageSubject = CRM_Utils_Token::replaceContactTokens($body_subject, $contact, FALSE, $tokens);
-      $messageSubject = CRM_Utils_Token::replaceDomainTokens($messageSubject, $domain, TRUE, $tokens);
-      $messageSubject = CRM_Utils_Token::replaceComponentTokens($messageSubject, $contact, $tokens, TRUE);
-      $messageSubject = CRM_Utils_Token::replaceHookTokens($messageSubject, $contact, $categories, TRUE);
-
-      $messageSubject = $smarty->fetch("string:{$messageSubject}");
-
-      // set up the parameters for CRM_Utils_Mail::send
-      $mailParams = [
-        'groupName' => 'Scheduled Reminder Sender',
-        'from' => $from,
-        'toName' => $contact['display_name'],
-        'toEmail' => $email,
-        'subject' => $messageSubject,
-      ];
-      if (!$html || $contact['preferred_mail_format'] == 'Text' ||
-        $contact['preferred_mail_format'] == 'Both'
-      ) {
-        // render the & entities in text mode, so that the links work
-        $mailParams['text'] = str_replace('&', '&', $text);
-      }
-      if ($html && ($contact['preferred_mail_format'] == 'HTML' ||
-          $contact['preferred_mail_format'] == 'Both'
-        )
-      ) {
-        $mailParams['html'] = $html;
-      }
-
-      $result = CRM_Utils_Mail::send($mailParams);
-    }
-
-    return $result;
-  }
-
   /**
    * Revert a message template to its default subject+text+HTML state.
    *
@@ -364,6 +245,37 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate {
     $diverted->save();
   }
 
+  /**
+   * Render a message template.
+   *
+   * This method is very similar to `sendTemplate()` - accepting most of the same arguments
+   * and emitting similar hooks. However, it specifically precludes the possibility of
+   * sending a message. It only renders.
+   *
+   * @param $params
+   *  Mixed render parameters. See sendTemplate() for more details.
+   * @return array
+   *   Rendered message, consistent of 'subject', 'text', 'html'
+   *   Ex: ['subject' => 'Hello Bob', 'text' => 'It\'s been so long since we sent you an automated notification!']
+   * @throws \API_Exception
+   * @throws \CRM_Core_Exception
+   * @see sendTemplate()
+   */
+  public static function renderTemplate($params) {
+    $forbidden = ['from', 'toName', 'toEmail', 'cc', 'bcc', 'replyTo'];
+    $intersect = array_intersect($forbidden, array_keys($params));
+    if (!empty($intersect)) {
+      throw new \CRM_Core_Exception(sprintf("renderTemplate() received forbidden fields (%s)",
+        implode(',', $intersect)));
+    }
+
+    $mailContent = [];
+    // sendTemplate has had an obscure feature - if you omit `toEmail`, then it merely renders.
+    // At some point, we may want to invert the relation between renderTemplate/sendTemplate, but for now this is a smaller patch.
+    [$sent, $mailContent['subject'], $mailContent['text'], $mailContent['html']] = static::sendTemplate($params);
+    return $mailContent;
+  }
+
   /**
    * Send an email from the specified template based on an array of params.
    *
@@ -376,22 +288,34 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate {
    * @throws \API_Exception
    */
   public static function sendTemplate($params) {
-    $defaults = [
-      // option value name of the template
+    $modelDefaults = [
+      // instance of WorkflowMessageInterface, containing a list of data to provide to the message-template
+      'model' => NULL,
+      // Symbolic name of the workflow step. Matches the option-value-name of the template.
       'valueName' => NULL,
-      // ID of the template
-      'messageTemplateID' => NULL,
-      // content of the message template
-      // Ex: ['msg_subject' => 'Hello {contact.display_name}', 'msg_html' => '...', 'msg_text' => '...']
-      // INTERNAL: 'messageTemplate' is currently only intended for use within civicrm-core only. For downstream usage, future updates will provide comparable public APIs.
-      'messageTemplate' => NULL,
-      // contact id if contact tokens are to be replaced
-      'contactId' => NULL,
       // additional template params (other than the ones already set in the template singleton)
       'tplParams' => [],
       // additional token params (passed to the TokenProcessor)
       // INTERNAL: 'tokenContext' is currently only intended for use within civicrm-core only. For downstream usage, future updates will provide comparable public APIs.
       'tokenContext' => [],
+      // properties to import directly to the model object
+      'modelProps' => NULL,
+      // contact id if contact tokens are to be replaced; alias for tokenContext.contactId
+      'contactId' => NULL,
+    ];
+    $viewDefaults = [
+      // ID of the specific template to load
+      'messageTemplateID' => NULL,
+      // content of the message template
+      // Ex: ['msg_subject' => 'Hello {contact.display_name}', 'msg_html' => '...', 'msg_text' => '...']
+      // INTERNAL: 'messageTemplate' is currently only intended for use within civicrm-core only. For downstream usage, future updates will provide comparable public APIs.
+      'messageTemplate' => NULL,
+      // whether this is a test email (and hence should include the test banner)
+      'isTest' => FALSE,
+      // Disable Smarty?
+      'disableSmarty' => FALSE,
+    ];
+    $envelopeDefaults = [
       // the From: header
       'from' => NULL,
       // the recipient’s name
@@ -406,28 +330,32 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate {
       'replyTo' => NULL,
       // email attachments
       'attachments' => NULL,
-      // whether this is a test email (and hence should include the test banner)
-      'isTest' => FALSE,
       // filename of optional PDF version to add as attachment (do not include path)
       'PDFFilename' => NULL,
-      // Disable Smarty?
-      'disableSmarty' => FALSE,
     ];
-    $params = array_merge($defaults, $params);
 
-    // Core#644 - handle Email ID passed as "From".
-    if (isset($params['from'])) {
-      $params['from'] = CRM_Utils_Mail::formatFromAddress($params['from']);
-    }
+    // Allow WorkflowMessage to run any filters/mappings/cleanups.
+    $model = $params['model'] ?? WorkflowMessage::create($params['valueName'] ?? 'UNKNOWN');
+    $params = WorkflowMessage::exportAll(WorkflowMessage::importAll($model, $params));
+    unset($params['model']);
+    // Subsequent hooks use $params. Retaining the $params['model'] might be nice - but don't do it unless you figure out how to ensure data-consistency (eg $params['tplParams'] <=> $params['model']).
+    // If you want to expose the model via hook, consider interjecting a new Hook::alterWorkflowMessage($model) between `importAll()` and `exportAll()`.
+
+    $params = array_merge($modelDefaults, $viewDefaults, $envelopeDefaults, $params);
 
     CRM_Utils_Hook::alterMailParams($params, 'messageTemplate');
-    if (!is_int($params['messageTemplateID']) && !is_null($params['messageTemplateID'])) {
-      CRM_Core_Error::deprecatedWarning('message template id should be an integer');
-      $params['messageTemplateID'] = (int) $params['messageTemplateID'];
-    }
     $mailContent = self::loadTemplate((string) $params['valueName'], $params['isTest'], $params['messageTemplateID'] ?? NULL, $params['groupName'] ?? '', $params['messageTemplate'], $params['subject'] ?? NULL);
 
-    $mailContent = self::renderMessageTemplate($mailContent, (bool) $params['disableSmarty'], $params['contactId'] ?? NULL, $params['tplParams'], $params['tokenContext']);
+    $params['tokenContext'] = array_merge([
+      'smarty' => (bool) !$params['disableSmarty'],
+      'contactId' => $params['contactId'],
+    ], $params['tokenContext']);
+    $rendered = CRM_Core_TokenSmarty::render(CRM_Utils_Array::subset($mailContent, ['text', 'html', 'subject']), $params['tokenContext'], $params['tplParams']);
+    if (isset($rendered['subject'])) {
+      $rendered['subject'] = trim(preg_replace('/[\r\n]+/', ' ', $rendered['subject']));
+    }
+    $nullSet = ['subject' => NULL, 'text' => NULL, 'html' => NULL];
+    $mailContent = array_merge($nullSet, $mailContent, $rendered);
 
     // send the template, honouring the target user’s preferences (if any)
     $sent = FALSE;
@@ -438,21 +366,24 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate {
     $params['html'] = $mailContent['html'];
 
     if ($params['toEmail']) {
-      $contactParams = [['email', 'LIKE', $params['toEmail'], 0, 1]];
-      [$contact] = CRM_Contact_BAO_Query::apiQuery($contactParams);
-
-      $prefs = array_pop($contact);
-
-      if (isset($prefs['preferred_mail_format']) and $prefs['preferred_mail_format'] === 'HTML') {
+      // @todo - consider whether we really should be loading
+      // this based on 'the first email in the db that matches'.
+      // when we likely have the contact id. OTOH people probably barely
+      // use preferredMailFormat these days - the good fight against html
+      // emails was lost a decade ago...
+      $preferredMailFormatArray = Email::get(FALSE)->addWhere('email', '=', $params['toEmail'])->addSelect('contact_id.preferred_mail_format')->execute()->first();
+      $preferredMailFormat = $preferredMailFormatArray['contact_id.preferred_mail_format'] ?? 'Both';
+
+      if ($preferredMailFormat === 'HTML') {
         $params['text'] = NULL;
       }
-
-      if (isset($prefs['preferred_mail_format']) and $prefs['preferred_mail_format'] === 'Text') {
+      if ($preferredMailFormat === 'Text') {
         $params['html'] = NULL;
       }
 
       $config = CRM_Core_Config::singleton();
       if (isset($params['isEmailPdf']) && $params['isEmailPdf'] == 1) {
+        // FIXME: $params['contributionId'] is not modeled in the parameter list. When is it supplied? Should probably move to tokenContext.contributionId.
         $pdfHtml = CRM_Contribute_BAO_ContributionPage::addInvoicePdfToEmail($params['contributionId'], $params['contactId']);
         if (empty($params['attachments'])) {
           $params['attachments'] = [];
@@ -580,33 +511,4 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate {
     return $mailContent;
   }
 
-  /**
-   * Render the message template, resolving tokens and smarty tokens.
-   *
-   * As with all BAO methods this should not be called directly outside
-   * of tested core code and is highly likely to change.
-   *
-   * @param array $mailContent
-   * @param bool $disableSmarty
-   * @param int|NULL $contactID
-   * @param array $smartyAssigns
-   *   Data to pass through to Smarty.
-   * @param array $tokenContext
-   *   Data to pass through to TokenProcessor.
-   *
-   * @return array
-   */
-  public static function renderMessageTemplate(array $mailContent, bool $disableSmarty, $contactID, array $smartyAssigns, array $tokenContext = []): array {
-    $tokenContext['smarty'] = !$disableSmarty;
-    if ($contactID && !isset($tokenContext['contactId'])) {
-      $tokenContext['contactId'] = $contactID;
-    }
-    $result = CRM_Core_TokenSmarty::render(CRM_Utils_Array::subset($mailContent, ['text', 'html', 'subject']), $tokenContext, $smartyAssigns);
-    if (isset($mailContent['subject'])) {
-      $result['subject'] = trim(preg_replace('/[\r\n]+/', ' ', $result['subject']));
-    }
-    $nullSet = ['subject' => NULL, 'text' => NULL, 'html' => NULL];
-    return array_merge($nullSet, $mailContent, $result);
-  }
-
 }