[Ref] Extend email trait test, process more sanely
[civicrm-core.git] / CRM / Contact / Form / Task / EmailTrait.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18 use Civi\Api4\Email;
19
20 /**
21 * This class provides the common functionality for tasks that send emails.
22 */
23 trait CRM_Contact_Form_Task_EmailTrait {
24
25 /**
26 * Are we operating in "single mode", i.e. sending email to one
27 * specific contact?
28 *
29 * @var bool
30 */
31 public $_single = FALSE;
32
33 /**
34 * All the existing templates in the system.
35 *
36 * @var array
37 */
38 public $_templates;
39
40 /**
41 * Email addresses to send to.
42 *
43 * @var array
44 */
45 protected $emails = [];
46
47 /**
48 * Store "to" contact details.
49 * @var array
50 */
51 public $_toContactDetails = [];
52
53 /**
54 * Store all selected contact id's, that includes to, cc and bcc contacts
55 * @var array
56 */
57 public $_allContactIds = [];
58
59 /**
60 * Store only "to" contact ids.
61 * @var array
62 */
63 public $_toContactIds = [];
64
65 /**
66 * Is the form being loaded from a search action.
67 *
68 * @var bool
69 */
70 public $isSearchContext = TRUE;
71
72 public $contactEmails = [];
73
74 /**
75 * Contacts form whom emails could not be sent.
76 *
77 * An array of contact ids and the relevant message details.
78 *
79 * @var array
80 */
81 protected $suppressedEmails = [];
82
83 /**
84 * Getter for isSearchContext.
85 *
86 * @return bool
87 */
88 public function isSearchContext(): bool {
89 return $this->isSearchContext;
90 }
91
92 /**
93 * Setter for isSearchContext.
94 *
95 * @param bool $isSearchContext
96 */
97 public function setIsSearchContext(bool $isSearchContext) {
98 $this->isSearchContext = $isSearchContext;
99 }
100
101 /**
102 * Build all the data structures needed to build the form.
103 *
104 * @throws \CiviCRM_API3_Exception
105 * @throws \CRM_Core_Exception
106 */
107 public function preProcess() {
108 $this->traitPreProcess();
109 }
110
111 /**
112 * Call trait preProcess function.
113 *
114 * This function exists as a transitional arrangement so classes overriding
115 * preProcess can still call it. Ideally it will be melded into preProcess
116 * later.
117 *
118 * @throws \CRM_Core_Exception
119 * @throws \API_Exception
120 */
121 protected function traitPreProcess() {
122 $this->preProcessFromAddress();
123 if ($this->isSearchContext()) {
124 // Currently only the contact email form is callable outside search context.
125 parent::preProcess();
126 }
127 $this->setContactIDs();
128 $this->assign('single', $this->_single);
129 if (CRM_Core_Permission::check('administer CiviCRM')) {
130 $this->assign('isAdmin', 1);
131 }
132 }
133
134 /**
135 * Pre Process Form Addresses to be used in Quickform
136 *
137 * @throws \API_Exception
138 * @throws \CRM_Core_Exception
139 */
140 protected function preProcessFromAddress(): void {
141 $form = $this;
142 $form->_emails = [];
143
144 // @TODO remove these line and to it somewhere more appropriate. Currently some classes (e.g Case
145 // are having to re-write contactIds afterwards due to this inappropriate variable setting
146 // If we don't have any contact IDs, use the logged in contact ID
147 $form->_contactIds = $form->_contactIds ?: [CRM_Core_Session::getLoggedInContactID()];
148 }
149
150 /**
151 * Build the form object.
152 *
153 * @throws \CRM_Core_Exception
154 */
155 public function buildQuickForm() {
156 // Suppress form might not be required but perhaps there was a risk some other process had set it to TRUE.
157 $this->assign('suppressForm', FALSE);
158 $this->assign('emailTask', TRUE);
159
160 $toArray = [];
161 $suppressedEmails = 0;
162 //here we are getting logged in user id as array but we need target contact id. CRM-5988
163 $cid = $this->get('cid');
164 if ($cid) {
165 $this->_contactIds = explode(',', $cid);
166 }
167 if (count($this->_contactIds) > 1) {
168 $this->_single = FALSE;
169 }
170 $this->bounceIfSimpleMailLimitExceeded(count($this->_contactIds));
171
172 $emailAttributes = [
173 'class' => 'huge',
174 ];
175 $to = $this->add('text', 'to', ts('To'), $emailAttributes, TRUE);
176
177 $this->addEntityRef('cc_id', ts('CC'), [
178 'entity' => 'Email',
179 'multiple' => TRUE,
180 ]);
181
182 $this->addEntityRef('bcc_id', ts('BCC'), [
183 'entity' => 'Email',
184 'multiple' => TRUE,
185 ]);
186
187 if ($to->getValue()) {
188 $this->_toContactIds = $this->_contactIds = [];
189 }
190 $setDefaults = TRUE;
191 if (property_exists($this, '_context') && $this->_context === 'standalone') {
192 $setDefaults = FALSE;
193 }
194
195 $this->_allContactIds = $this->_toContactIds = $this->_contactIds;
196
197 if ($to->getValue()) {
198 foreach ($this->getEmails($to) as $value) {
199 $contactId = $value['contact_id'];
200 if ($contactId) {
201 $this->_contactIds[] = $this->_toContactIds[] = $contactId;
202 $this->_allContactIds[] = $contactId;
203 }
204 }
205 $setDefaults = TRUE;
206 }
207
208 //get the group of contacts as per selected by user in case of Find Activities
209 if (!empty($this->_activityHolderIds)) {
210 $contact = $this->get('contacts');
211 $this->_allContactIds = $this->_contactIds = $contact;
212 }
213
214 // check if we need to setdefaults and check for valid contact emails / communication preferences
215 if (is_array($this->_allContactIds) && $setDefaults) {
216 // get the details for all selected contacts ( to, cc and bcc contacts )
217 $allContactDetails = civicrm_api3('Contact', 'get', [
218 'id' => ['IN' => $this->_allContactIds],
219 'return' => ['sort_name', 'email', 'do_not_email', 'is_deceased', 'on_hold', 'display_name', 'preferred_mail_format'],
220 'options' => ['limit' => 0],
221 ])['values'];
222
223 // The contact task supports passing in email_id in a url. It supports a single email
224 // and is marked as having been related to CiviHR.
225 // The array will look like $this->_toEmail = ['email' => 'x', 'contact_id' => 2])
226 // If it exists we want to use the specified email which might be different to the primary email
227 // that we have.
228 if (!empty($this->_toEmail['contact_id']) && !empty($allContactDetails[$this->_toEmail['contact_id']])) {
229 $allContactDetails[$this->_toEmail['contact_id']]['email'] = $this->_toEmail['email'];
230 }
231
232 // perform all validations on unique contact Ids
233 foreach ($allContactDetails as $contactId => $value) {
234 if ($value['do_not_email'] || empty($value['email']) || !empty($value['is_deceased']) || $value['on_hold']) {
235 $this->setSuppressedEmail($contactId, $value);
236 }
237 elseif (in_array($contactId, $this->_toContactIds)) {
238 $this->_toContactDetails[$contactId] = $this->_contactDetails[$contactId] = $value;
239 $toArray[] = [
240 'text' => '"' . $value['sort_name'] . '" <' . $value['email'] . '>',
241 'id' => "$contactId::{$value['email']}",
242 ];
243 }
244 }
245
246 if (empty($toArray)) {
247 CRM_Core_Error::statusBounce(ts('Selected contact(s) do not have a valid email address, or communication preferences specify DO NOT EMAIL, or they are deceased or Primary email address is On Hold.'));
248 }
249 }
250
251 $this->assign('toContact', json_encode($toArray));
252
253 $this->assign('suppressedEmails', count($this->suppressedEmails));
254
255 $this->assign('totalSelectedContacts', count($this->_contactIds));
256
257 $this->add('text', 'subject', ts('Subject'), ['size' => 50, 'maxlength' => 254], TRUE);
258
259 $this->add('select', 'from_email_address', ts('From'), $this->getFromEmails(), TRUE);
260
261 CRM_Mailing_BAO_Mailing::commonCompose($this);
262
263 // add attachments
264 CRM_Core_BAO_File::buildAttachment($this, NULL);
265
266 if ($this->_single) {
267 // also fix the user context stack
268 if ($this->getCaseID()) {
269 $ccid = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseContact', $this->_caseId,
270 'contact_id', 'case_id'
271 );
272 $url = CRM_Utils_System::url('civicrm/contact/view/case',
273 "&reset=1&action=view&cid={$ccid}&id=" . $this->getCaseID()
274 );
275 }
276 elseif ($this->_context) {
277 $url = CRM_Utils_System::url('civicrm/dashboard', 'reset=1');
278 }
279 else {
280 $url = CRM_Utils_System::url('civicrm/contact/view',
281 "&show=1&action=browse&cid={$this->_contactIds[0]}&selectedChild=activity"
282 );
283 }
284
285 $session = CRM_Core_Session::singleton();
286 $session->replaceUserContext($url);
287 }
288 $this->addDefaultButtons(ts('Send Email'), 'upload', 'cancel');
289
290 $fields = [
291 'followup_assignee_contact_id' => [
292 'type' => 'entityRef',
293 'label' => ts('Assigned to'),
294 'attributes' => [
295 'multiple' => TRUE,
296 'create' => TRUE,
297 'api' => ['params' => ['is_deceased' => 0]],
298 ],
299 ],
300 'followup_activity_type_id' => [
301 'type' => 'select',
302 'label' => ts('Followup Activity'),
303 'attributes' => ['' => '- ' . ts('select activity') . ' -'] + CRM_Core_PseudoConstant::ActivityType(FALSE),
304 'extra' => ['class' => 'crm-select2'],
305 ],
306 'followup_activity_subject' => [
307 'type' => 'text',
308 'label' => ts('Subject'),
309 'attributes' => CRM_Core_DAO::getAttribute('CRM_Activity_DAO_Activity',
310 'subject'
311 ),
312 ],
313 ];
314
315 //add followup date
316 $this->add('datepicker', 'followup_date', ts('in'));
317
318 foreach ($fields as $field => $values) {
319 if (!empty($fields[$field])) {
320 $attribute = $values['attributes'] ?? NULL;
321 $required = !empty($values['required']);
322
323 if ($values['type'] === 'select' && empty($attribute)) {
324 $this->addSelect($field, ['entity' => 'activity'], $required);
325 }
326 elseif ($values['type'] === 'entityRef') {
327 $this->addEntityRef($field, $values['label'], $attribute, $required);
328 }
329 else {
330 $this->add($values['type'], $field, $values['label'], $attribute, $required, CRM_Utils_Array::value('extra', $values));
331 }
332 }
333 }
334
335 //Added for CRM-15984: Add campaign field
336 CRM_Campaign_BAO_Campaign::addCampaign($this);
337
338 $this->addFormRule([__CLASS__, 'saveTemplateFormRule'], $this);
339 $this->addFormRule([__CLASS__, 'deprecatedTokensFormRule'], $this);
340 CRM_Core_Resources::singleton()->addScriptFile('civicrm', 'templates/CRM/Contact/Form/Task/EmailCommon.js', 0, 'html-header');
341 }
342
343 /**
344 * Set relevant default values.
345 *
346 * @return array
347 *
348 * @throws \API_Exception
349 * @throws \CRM_Core_Exception
350 */
351 public function setDefaultValues(): array {
352 $defaults = parent::setDefaultValues();
353 $fromEmails = $this->getFromEmails();
354 if (is_numeric(key($fromEmails))) {
355 $emailID = (int) key($fromEmails);
356 $defaults = CRM_Core_BAO_Email::getEmailSignatureDefaults($emailID);
357 }
358 if (!Civi::settings()->get('allow_mail_from_logged_in_contact')) {
359 $defaults['from_email_address'] = current(CRM_Core_BAO_Domain::getNameAndEmail(FALSE, TRUE));
360 }
361 return $defaults;
362 }
363
364 /**
365 * Process the form after the input has been submitted and validated.
366 *
367 * @throws \CRM_Core_Exception
368 * @throws \CiviCRM_API3_Exception
369 * @throws \Civi\API\Exception\UnauthorizedException
370 * @throws \API_Exception
371 */
372 public function postProcess() {
373 $this->bounceIfSimpleMailLimitExceeded(count($this->_contactIds));
374 $formValues = $this->controller->exportValues($this->getName());
375 $this->submit($formValues);
376 }
377
378 /**
379 * Bounce if there are more emails than permitted.
380 *
381 * @param int $count
382 * The number of emails the user is attempting to send
383 */
384 protected function bounceIfSimpleMailLimitExceeded($count): void {
385 $limit = Civi::settings()->get('simple_mail_limit');
386 if ($count > $limit) {
387 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.',
388 [1 => $limit]
389 ));
390 }
391 }
392
393 /**
394 * Submit the form values.
395 *
396 * This is also accessible for testing.
397 *
398 * @param array $formValues
399 *
400 * @throws \CRM_Core_Exception
401 * @throws \CiviCRM_API3_Exception
402 * @throws \Civi\API\Exception\UnauthorizedException
403 * @throws \API_Exception
404 */
405 public function submit($formValues): void {
406 $this->saveMessageTemplate($formValues);
407
408 $from = $formValues['from_email_address'] ?? NULL;
409 // dev/core#357 User Emails are keyed by their id so that the Signature is able to be added
410 // If we have had a contact email used here the value returned from the line above will be the
411 // numerical key where as $from for use in the sendEmail in Activity needs to be of format of "To Name" <toemailaddress>
412 $from = CRM_Utils_Mail::formatFromAddress($from);
413
414 $ccArray = $formValues['cc_id'] ? explode(',', $formValues['cc_id']) : [];
415 $cc = $this->getEmailString($ccArray);
416 $additionalDetails = empty($ccArray) ? '' : "\ncc : " . $this->getEmailUrlString($ccArray);
417
418 $bccArray = $formValues['bcc_id'] ? explode(',', $formValues['bcc_id']) : [];
419 $bcc = $this->getEmailString($bccArray);
420 $additionalDetails .= empty($bccArray) ? '' : "\nbcc : " . $this->getEmailUrlString($bccArray);
421
422 // format contact details array to handle multiple emails from same contact
423 $formattedContactDetails = [];
424 foreach ($this->_contactIds as $key => $contactId) {
425 // if we dont have details on this contactID, we should ignore
426 // potentially this is due to the contact not wanting to receive email
427 if (!isset($this->_contactDetails[$contactId])) {
428 continue;
429 }
430 $email = $this->getEmail($key);
431 // prevent duplicate emails if same email address is selected CRM-4067
432 // we should allow same emails for different contacts
433 $details = $this->_contactDetails[$contactId];
434 $details['email'] = $email;
435 unset($details['email_id']);
436 $formattedContactDetails["{$contactId}::{$email}"] = $details;
437 }
438
439 // send the mail
440 [$sent, $activityIds] = CRM_Activity_BAO_Activity::sendEmail(
441 $formattedContactDetails,
442 $this->getSubject($formValues['subject']),
443 $formValues['text_message'],
444 $formValues['html_message'],
445 NULL,
446 NULL,
447 $from,
448 $this->getAttachments($formValues),
449 $cc,
450 $bcc,
451 array_keys($this->_toContactDetails),
452 $additionalDetails,
453 $this->getContributionIDs(),
454 CRM_Utils_Array::value('campaign_id', $formValues),
455 $this->getCaseID()
456 );
457
458 if ($sent) {
459 // Only use the first activity id if there's multiple.
460 // If there's multiple recipients the idea behind multiple activities
461 // is to record the token value replacements separately, but that
462 // has no meaning for followup activities, and this doesn't prevent
463 // creating more manually if desired.
464 $followupStatus = $this->createFollowUpActivities($formValues, $activityIds[0]);
465 $count_success = count($this->_toContactDetails);
466 CRM_Core_Session::setStatus(ts('One message was sent successfully. ', [
467 'plural' => '%count messages were sent successfully. ',
468 'count' => $count_success,
469 ]) . $followupStatus, ts('Message Sent', ['plural' => 'Messages Sent', 'count' => $count_success]), 'success');
470 }
471
472 if (!empty($this->suppressedEmails)) {
473 $status = '(' . ts('because no email address on file or communication preferences specify DO NOT EMAIL or Contact is deceased or Primary email address is On Hold') . ')<ul><li>' . implode('</li><li>', $this->suppressedEmails) . '</li></ul>';
474 CRM_Core_Session::setStatus($status, ts('One Message Not Sent', [
475 'count' => count($this->suppressedEmails),
476 'plural' => '%count Messages Not Sent',
477 ]), 'info');
478 }
479 }
480
481 /**
482 * Save the template if update selected.
483 *
484 * @param array $formValues
485 *
486 * @throws \CiviCRM_API3_Exception
487 * @throws \Civi\API\Exception\UnauthorizedException
488 */
489 protected function saveMessageTemplate($formValues) {
490 if (!empty($formValues['saveTemplate']) || !empty($formValues['updateTemplate'])) {
491 $messageTemplate = [
492 'msg_text' => $formValues['text_message'],
493 'msg_html' => $formValues['html_message'],
494 'msg_subject' => $formValues['subject'],
495 'is_active' => TRUE,
496 ];
497
498 if (!empty($formValues['saveTemplate'])) {
499 $messageTemplate['msg_title'] = $formValues['saveTemplateName'];
500 CRM_Core_BAO_MessageTemplate::add($messageTemplate);
501 }
502
503 if (!empty($formValues['template']) && !empty($formValues['updateTemplate'])) {
504 $messageTemplate['id'] = $formValues['template'];
505 unset($messageTemplate['msg_title']);
506 CRM_Core_BAO_MessageTemplate::add($messageTemplate);
507 }
508 }
509 }
510
511 /**
512 * List available tokens for this form.
513 *
514 * @return array
515 */
516 public function listTokens() {
517 return CRM_Core_SelectValues::contactTokens();
518 }
519
520 /**
521 * Get the emails from the added element.
522 *
523 * @param HTML_QuickForm_Element $element
524 *
525 * @return array
526 */
527 protected function getEmails($element): array {
528 $allEmails = explode(',', $element->getValue());
529 $return = [];
530 foreach ($allEmails as $value) {
531 $values = explode('::', $value);
532 $return[] = ['contact_id' => $values[0], 'email' => $values[1]];
533 }
534 return $return;
535 }
536
537 /**
538 * Get the string for the email IDs.
539 *
540 * @param array $emailIDs
541 * Array of email IDs.
542 *
543 * @return string
544 * e.g. "Smith, Bob<bob.smith@example.com>".
545 *
546 * @throws \API_Exception
547 * @throws \Civi\API\Exception\UnauthorizedException
548 */
549 protected function getEmailString(array $emailIDs): string {
550 if (empty($emailIDs)) {
551 return '';
552 }
553 $emails = Email::get()
554 ->addWhere('id', 'IN', $emailIDs)
555 ->setCheckPermissions(FALSE)
556 ->setSelect(['contact_id', 'email', 'contact_id.sort_name', 'contact_id.display_name'])->execute();
557 $emailStrings = [];
558 foreach ($emails as $email) {
559 $this->contactEmails[$email['id']] = $email;
560 $emailStrings[] = '"' . $email['contact_id.sort_name'] . '" <' . $email['email'] . '>';
561 }
562 return implode(',', $emailStrings);
563 }
564
565 /**
566 * Get the url string.
567 *
568 * This is called after the contacts have been retrieved so we don't need to re-retrieve.
569 *
570 * @param array $emailIDs
571 *
572 * @return string
573 * e.g. <a href='{$contactURL}'>Bob Smith</a>'
574 */
575 protected function getEmailUrlString(array $emailIDs): string {
576 $urls = [];
577 foreach ($emailIDs as $email) {
578 $contactURL = CRM_Utils_System::url('civicrm/contact/view', ['reset' => 1, 'cid' => $this->contactEmails[$email]['contact_id']], TRUE);
579 $urls[] = "<a href='{$contactURL}'>" . $this->contactEmails[$email]['contact_id.display_name'] . '</a>';
580 }
581 return implode(', ', $urls);
582 }
583
584 /**
585 * Set the emails that are not to be sent out.
586 *
587 * @param int $contactID
588 * @param array $values
589 */
590 protected function setSuppressedEmail($contactID, $values) {
591 $contactViewUrl = CRM_Utils_System::url('civicrm/contact/view', 'reset=1&cid=' . $contactID);
592 $this->suppressedEmails[$contactID] = "<a href='$contactViewUrl' title='{$values['email']}'>{$values['display_name']}</a>" . ($values['on_hold'] ? '(' . ts('on hold') . ')' : '');
593 }
594
595 /**
596 * Get any attachments.
597 *
598 * @param array $formValues
599 *
600 * @return array
601 */
602 protected function getAttachments(array $formValues): array {
603 $attachments = [];
604 CRM_Core_BAO_File::formatAttachment($formValues,
605 $attachments,
606 NULL, NULL
607 );
608 return $attachments;
609 }
610
611 /**
612 * Get the subject for the message.
613 *
614 * The case handling should possibly be on the case form.....
615 *
616 * @param string $subject
617 *
618 * @return string
619 * @throws \CRM_Core_Exception
620 */
621 protected function getSubject(string $subject):string {
622 // CRM-5916: prepend case id hash to CiviCase-originating emails’ subjects
623 if ($this->getCaseID()) {
624 $hash = substr(sha1(CIVICRM_SITE_KEY . $this->getCaseID()), 0, 7);
625 $subject = "[case #$hash] $subject";
626 }
627 return $subject;
628 }
629
630 /**
631 * Create any follow up activities.
632 *
633 * @param array $formValues
634 * @param int $activityId
635 *
636 * @return string
637 *
638 * @throws \CRM_Core_Exception
639 */
640 protected function createFollowUpActivities($formValues, $activityId): string {
641 $params = [];
642 $followupStatus = '';
643 $followupActivity = NULL;
644 if (!empty($formValues['followup_activity_type_id'])) {
645 $params['followup_activity_type_id'] = $formValues['followup_activity_type_id'];
646 $params['followup_activity_subject'] = $formValues['followup_activity_subject'];
647 $params['followup_date'] = $formValues['followup_date'];
648 $params['target_contact_id'] = $this->_contactIds;
649 $params['followup_assignee_contact_id'] = array_filter(explode(',', $formValues['followup_assignee_contact_id']));
650 $followupActivity = CRM_Activity_BAO_Activity::createFollowupActivity($activityId, $params);
651 $followupStatus = ts('A followup activity has been scheduled.');
652
653 if (Civi::settings()->get('activity_assignee_notification')) {
654 if ($followupActivity) {
655 $mailToFollowupContacts = [];
656 $assignee = [$followupActivity->id];
657 $assigneeContacts = CRM_Activity_BAO_ActivityAssignment::getAssigneeNames($assignee, TRUE, FALSE);
658 foreach ($assigneeContacts as $values) {
659 $mailToFollowupContacts[$values['email']] = $values;
660 }
661
662 $sentFollowup = CRM_Activity_BAO_Activity::sendToAssignee($followupActivity, $mailToFollowupContacts);
663 if ($sentFollowup) {
664 $followupStatus .= '<br />' . ts('A copy of the follow-up activity has also been sent to follow-up assignee contacts(s).');
665 }
666 }
667 }
668 }
669 return $followupStatus;
670 }
671
672 /**
673 * Form rule.
674 *
675 * @param array $fields
676 * The input form values.
677 *
678 * @return bool|array
679 * true if no errors, else array of errors
680 */
681 public static function saveTemplateFormRule(array $fields) {
682 $errors = [];
683 //Added for CRM-1393
684 if (!empty($fields['saveTemplate']) && empty($fields['saveTemplateName'])) {
685 $errors['saveTemplateName'] = ts('Enter name to save message template');
686 }
687 return empty($errors) ? TRUE : $errors;
688 }
689
690 /**
691 * Prevent submission of deprecated tokens.
692 *
693 * @param array $fields
694 *
695 * @return bool|string[]
696 */
697 public static function deprecatedTokensFormRule(array $fields) {
698 $deprecatedTokens = [
699 '{case.status_id}' => '{case.status_id:label}',
700 '{case.case_type_id}' => '{case.case_type_id:label}',
701 ];
702 $tokenErrors = [];
703 foreach ($deprecatedTokens as $token => $replacement) {
704 if (strpos($fields['html_message'], $token) !== FALSE) {
705 $tokenErrors[] = ts('Token %1 is no longer supported - use %2 instead', [$token, $replacement]);
706 }
707 }
708 return empty($tokenErrors) ? TRUE : ['html_message' => implode('<br>', $tokenErrors)];
709 }
710
711 /**
712 * Get selected contribution IDs.
713 *
714 * @return array
715 */
716 protected function getContributionIDs(): array {
717 return [];
718 }
719
720 /**
721 * Get case ID - if any.
722 *
723 * @return int|null
724 *
725 * @throws \CRM_Core_Exception
726 */
727 protected function getCaseID(): ?int {
728 $caseID = CRM_Utils_Request::retrieve('caseid', 'String', $this);
729 if ($caseID) {
730 return (int) $caseID;
731 }
732 return NULL;
733 }
734
735 /**
736 * @return array
737 */
738 protected function getFromEmails(): array {
739 $fromEmailValues = CRM_Core_BAO_Email::getFromEmail();
740
741 if (empty($fromEmailValues)) {
742 CRM_Core_Error::statusBounce(ts('Your user record does not have a valid email address and no from addresses have been configured.'));
743 }
744 return $fromEmailValues;
745 }
746
747 /**
748 * Get the relevant emails.
749 *
750 * @param int $index
751 *
752 * @return string
753 */
754 protected function getEmail(int $index): string {
755 if (empty($this->emails)) {
756 $toEmails = explode(',', $this->getSubmittedValue('to'));
757 foreach ($toEmails as $value) {
758 $parts = explode('::', $value);
759 $this->emails[] = $parts[1];
760 }
761 }
762 return $this->emails[$index];
763 }
764
765 }