Merge pull request #21657 from totten/master-wfvn
[civicrm-core.git] / tests / phpunit / CRM / Utils / TokenConsistencyTest.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 use Civi\Token\TokenProcessor;
13 use Civi\Api4\LocBlock;
14 use Civi\Api4\Email;
15 use Civi\Api4\Phone;
16 use Civi\Api4\Address;
17
18 /**
19 * CRM_Utils_TokenConsistencyTest
20 *
21 * Class for ensuring tokens have internal consistency.
22 *
23 * @group Tokens
24 *
25 * @group headless
26 */
27 class CRM_Utils_TokenConsistencyTest extends CiviUnitTestCase {
28
29 use CRMTraits_Custom_CustomDataTrait;
30
31 /**
32 * Created case.
33 *
34 * @var array
35 */
36 protected $case;
37
38 /**
39 * Recurring contribution.
40 *
41 * @var array
42 */
43 protected $contributionRecur;
44
45 /**
46 * Post test cleanup.
47 *
48 * @throws \API_Exception
49 * @throws \CRM_Core_Exception
50 */
51 public function tearDown(): void {
52 $this->quickCleanup(['civicrm_case', 'civicrm_case_type'], TRUE);
53 parent::tearDown();
54 }
55
56 /**
57 * Test that case tokens are consistently rendered.
58 *
59 * @throws \CiviCRM_API3_Exception
60 */
61 public function testCaseTokenConsistency(): void {
62 $this->createLoggedInUser();
63 CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase');
64 $this->createCustomGroupWithFieldOfType(['extends' => 'Case']);
65 $tokens = CRM_Core_SelectValues::caseTokens();
66 $this->assertEquals($this->getCaseTokens(), $tokens);
67 $caseID = $this->getCaseID();
68 $tokenString = implode("\n", array_keys($this->getCaseTokens()));
69 $tokenHtml = CRM_Utils_Token::replaceCaseTokens($caseID, $tokenString, ['case' => $this->getCaseTokenKeys()]);
70 $this->assertEquals($this->getExpectedCaseTokenOutput(), $tokenHtml);
71 // Now do the same without passing in 'knownTokens'
72 $tokenHtml = CRM_Utils_Token::replaceCaseTokens($caseID, $tokenString);
73 $this->assertEquals($this->getExpectedCaseTokenOutput(), $tokenHtml);
74
75 // And check our deprecated tokens still work.
76 $tokenHtml = CRM_Utils_Token::replaceCaseTokens($caseID, '{case.case_type_id} {case.status_id}');
77 $this->assertEquals('Housing Support Ongoing', $tokenHtml);
78
79 $additionalTokensFromProcessor = [
80 '{case.case_type_id}' => 'Case Type ID',
81 '{case.status_id}' => 'Case Status',
82 '{case.case_type_id:name}' => 'Machine name: Case Type',
83 '{case.status_id:name}' => 'Machine name: Case Status',
84 ];
85 $expectedTokens = array_merge($this->getCaseTokens(), $additionalTokensFromProcessor);
86
87 $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
88 'controller' => __CLASS__,
89 'smarty' => FALSE,
90 'schema' => ['caseId'],
91 ]);
92 $this->assertEquals(array_merge($expectedTokens, $this->getDomainTokens()), $tokenProcessor->listTokens());
93 $tokenProcessor->addRow([
94 'caseId' => $this->getCaseID(),
95 ]);
96 $tokenProcessor->addMessage('html', $tokenString, 'text/plain');
97
98 $tokenProcessor->evaluate();
99 foreach ($tokenProcessor->getRows() as $row) {
100 $text = $row->render('html');
101 }
102 $this->assertEquals($this->getExpectedCaseTokenOutput(), $text);
103 }
104
105 /**
106 * Get expected output from token parsing.
107 *
108 * @return string
109 */
110 protected function getExpectedCaseTokenOutput(): string {
111 return '1
112 Housing Support
113 Case Subject
114 July 23rd, 2021
115 July 26th, 2021
116 case details
117 Ongoing
118 No
119 ' . CRM_Utils_Date::customFormat($this->case['created_date']) . '
120 ' . CRM_Utils_Date::customFormat($this->case['modified_date']) . '
121 ';
122 }
123
124 /**
125 * @return int
126 */
127 protected function getContactID(): int {
128 if (!isset($this->ids['Contact'][0])) {
129 $this->ids['Contact'][0] = $this->individualCreate();
130 }
131 return $this->ids['Contact'][0];
132 }
133
134 /**
135 * Get the keys for the case tokens.
136 *
137 * @return array
138 */
139 public function getCaseTokenKeys(): array {
140 $return = [];
141 foreach (array_keys($this->getCaseTokens()) as $key) {
142 $return[] = substr($key, 6, -1);
143 }
144 return $return;
145 }
146
147 /**
148 * Get declared tokens.
149 *
150 * @return string[]
151 */
152 public function getCaseTokens(): array {
153 return [
154 '{case.id}' => 'Case ID',
155 '{case.case_type_id:label}' => 'Case Type',
156 '{case.subject}' => 'Case Subject',
157 '{case.start_date}' => 'Case Start Date',
158 '{case.end_date}' => 'Case End Date',
159 '{case.details}' => 'Details',
160 '{case.status_id:label}' => 'Case Status',
161 '{case.is_deleted:label}' => 'Case is in the Trash',
162 '{case.created_date}' => 'Created Date',
163 '{case.modified_date}' => 'Modified Date',
164 '{case.custom_1}' => 'Enter text here :: Group with field text',
165 ];
166 }
167
168 /**
169 * Get case ID.
170 *
171 * @return int
172 */
173 protected function getCaseID(): int {
174 if (!isset($this->case)) {
175 $case_id = $this->callAPISuccess('Case', 'create', [
176 'case_type_id' => 'housing_support',
177 'activity_subject' => 'Case Subject',
178 'client_id' => $this->getContactID(),
179 'status_id' => 1,
180 'subject' => 'Case Subject',
181 'start_date' => '2021-07-23 15:39:20',
182 // Note end_date is inconsistent with status Ongoing but for the
183 // purposes of testing tokens is ok. Creating it with status Resolved
184 // then ignores our known fixed end date.
185 'end_date' => '2021-07-26 18:07:20',
186 'medium_id' => 2,
187 'details' => 'case details',
188 'activity_details' => 'blah blah',
189 'sequential' => 1,
190 ])['id'];
191 // Need to retrieve the case again because modified date might be updated a
192 // split-second later than the original return value because of activity
193 // triggers when the timeline is populated. The returned array from create
194 // is determined before that happens.
195 $this->case = $this->callAPISuccess('Case', 'getsingle', ['id' => $case_id]);
196 }
197 return $this->case['id'];
198 }
199
200 /**
201 * Test that contribution recur tokens are consistently rendered.
202 */
203 public function testContributionRecurTokenConsistency(): void {
204 $this->createLoggedInUser();
205 $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
206 'controller' => __CLASS__,
207 'smarty' => FALSE,
208 'schema' => ['contribution_recurId'],
209 ]);
210 $this->assertEquals(array_merge($this->getContributionRecurTokens(), $this->getDomainTokens()), $tokenProcessor->listTokens());
211 $tokenString = implode("\n", array_keys($this->getContributionRecurTokens()));
212
213 $tokenProcessor->addMessage('html', $tokenString, 'text/plain');
214 $tokenProcessor->addRow(['contribution_recurId' => $this->getContributionRecurID()]);
215 $tokenProcessor->evaluate();
216 $this->assertEquals($this->getExpectedContributionRecurTokenOutPut(), $tokenProcessor->getRow(0)->render('html'));
217 }
218
219 /**
220 * Get the contribution recur tokens keyed by the token.
221 *
222 * e.g {contribution_recur.id}
223 *
224 * @return array
225 */
226 protected function getContributionRecurTokens(): array {
227 $return = [];
228 foreach ($this->getContributionRecurTokensByField() as $key => $value) {
229 $return['{contribution_recur.' . $key . '}'] = $value;
230 }
231 return $return;
232 }
233
234 protected function getContributionRecurTokensByField(): array {
235 return [
236 'id' => 'Recurring Contribution ID',
237 'amount' => 'Amount',
238 'currency' => 'Currency',
239 'frequency_unit' => 'Frequency Unit',
240 'frequency_interval' => 'Interval (number of units)',
241 'installments' => 'Number of Installments',
242 'start_date' => 'Start Date',
243 'create_date' => 'Created Date',
244 'modified_date' => 'Modified Date',
245 'cancel_date' => 'Cancel Date',
246 'cancel_reason' => 'Cancellation Reason',
247 'end_date' => 'Recurring Contribution End Date',
248 'processor_id' => 'Processor ID',
249 'payment_token_id' => 'Payment Token ID',
250 'trxn_id' => 'Transaction ID',
251 'invoice_id' => 'Invoice ID',
252 'contribution_status_id' => 'Status',
253 'is_test:label' => 'Test',
254 'cycle_day' => 'Cycle Day',
255 'next_sched_contribution_date' => 'Next Scheduled Contribution Date',
256 'failure_count' => 'Number of Failures',
257 'failure_retry_date' => 'Retry Failed Attempt Date',
258 'auto_renew:label' => 'Auto Renew',
259 'payment_processor_id' => 'Payment Processor ID',
260 'financial_type_id' => 'Financial Type ID',
261 'payment_instrument_id' => 'Payment Method',
262 'is_email_receipt:label' => 'Send email Receipt?',
263 'frequency_unit:label' => 'Frequency Unit',
264 'frequency_unit:name' => 'Machine name: Frequency Unit',
265 'contribution_status_id:label' => 'Status',
266 'contribution_status_id:name' => 'Machine name: Status',
267 'payment_processor_id:label' => 'Payment Processor',
268 'payment_processor_id:name' => 'Machine name: Payment Processor',
269 'financial_type_id:label' => 'Financial Type',
270 'financial_type_id:name' => 'Machine name: Financial Type',
271 'payment_instrument_id:label' => 'Payment Method',
272 'payment_instrument_id:name' => 'Machine name: Payment Method',
273 ];
274 }
275
276 /**
277 * Get contributionRecur ID.
278 *
279 * @return int
280 */
281 protected function getContributionRecurID(): int {
282 if (!isset($this->contributionRecur)) {
283 $paymentProcessorID = $this->processorCreate();
284 $this->contributionRecur = $this->callAPISuccess('ContributionRecur', 'create', [
285 'contact_id' => $this->getContactID(),
286 'status_id' => 1,
287 'is_email_receipt' => 1,
288 'start_date' => '2021-07-23 15:39:20',
289 'end_date' => '2021-07-26 18:07:20',
290 'cancel_date' => '2021-08-19 09:12:45',
291 'next_sched_contribution_date' => '2021-09-08',
292 'cancel_reason' => 'Because',
293 'amount' => 5990.99,
294 'currency' => 'EUR',
295 'frequency_unit' => 'year',
296 'frequency_interval' => 2,
297 'installments' => 24,
298 'payment_instrument_id' => 'Check',
299 'financial_type_id' => 'Member dues',
300 'processor_id' => 'abc',
301 'payment_processor_id' => $paymentProcessorID,
302 'trxn_id' => 123,
303 'invoice_id' => 'inv123',
304 'sequential' => 1,
305 'failure_retry_date' => '2020-01-03',
306 'auto_renew' => 1,
307 'cycle_day' => '15',
308 'is_test' => TRUE,
309 'payment_token_id' => $this->callAPISuccess('PaymentToken', 'create', [
310 'contact_id' => $this->getContactID(),
311 'token' => 456,
312 'payment_processor_id' => $paymentProcessorID,
313 ])['id'],
314 ])['values'][0];
315 }
316 return $this->contributionRecur['id'];
317 }
318
319 /**
320 * Get rendered output for contribution tokens.
321 *
322 * @return string
323 */
324 protected function getExpectedContributionRecurTokenOutPut(): string {
325 return $this->getContributionRecurID() . '
326 € 5,990.99
327 EUR
328 year
329 2
330 24
331 July 23rd, 2021 3:39 PM
332 ' . CRM_Utils_Date::customFormat($this->contributionRecur['create_date']) . '
333 ' . CRM_Utils_Date::customFormat($this->contributionRecur['modified_date']) . '
334 August 19th, 2021 9:12 AM
335 Because
336 July 26th, 2021 6:07 PM
337 abc
338 1
339 123
340 inv123
341 2
342 Yes
343 15
344 September 8th, 2021
345 0
346 January 3rd, 2020
347 Yes
348 1
349 2
350 4
351 Yes
352 year
353 year
354 Pending Label**
355 Pending
356 Dummy (test)
357 Dummy (test)
358 Member Dues
359 Member Dues
360 Check
361 Check';
362 }
363
364 /**
365 * Test that membership tokens are consistently rendered.
366 *
367 * @throws \API_Exception
368 */
369 public function testMembershipTokenConsistency(): void {
370 $this->createLoggedInUser();
371 $this->restoreMembershipTypes();
372 $this->createCustomGroupWithFieldOfType(['extends' => 'Membership']);
373 $tokens = CRM_Core_SelectValues::membershipTokens();
374 $expectedTokens = $this->getMembershipTokens();
375 $this->assertEquals($expectedTokens, $tokens);
376 $newStyleTokens = "\n{membership.status_id:label}\n{membership.membership_type_id:label}\n";
377 $tokenString = $newStyleTokens . implode("\n", array_keys($this->getMembershipTokens()));
378
379 $memberships = CRM_Utils_Token::getMembershipTokenDetails([$this->getMembershipID()]);
380 $messageToken = CRM_Utils_Token::getTokens($tokenString);
381 $tokenHtml = CRM_Utils_Token::replaceEntityTokens('membership', $memberships[$this->getMembershipID()], $tokenString, $messageToken);
382 $this->assertEquals($this->getExpectedMembershipTokenOutput(), $tokenHtml);
383
384 // Custom fields work in the processor so test it....
385 $tokenString .= "\n{membership." . $this->getCustomFieldName('text') . '}';
386 // Now compare with scheduled reminder
387 $mut = new CiviMailUtils($this);
388 CRM_Utils_Time::setTime('2007-01-22 15:00:00');
389 $this->callAPISuccess('action_schedule', 'create', [
390 'title' => 'job',
391 'subject' => 'job',
392 'entity_value' => 1,
393 'mapping_id' => 4,
394 'start_action_date' => 'membership_join_date',
395 'start_action_offset' => 1,
396 'start_action_condition' => 'after',
397 'start_action_unit' => 'day',
398 'body_html' => $tokenString,
399 ]);
400 $this->callAPISuccess('job', 'send_reminder', []);
401 $expected = $this->getExpectedMembershipTokenOutput();
402 // Unlike the legacy method custom fields are resolved by the processor.
403 $expected .= "\nmy field";
404 $mut->checkMailLog([$expected]);
405
406 $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
407 'controller' => __CLASS__,
408 'smarty' => FALSE,
409 'schema' => ['membershipId'],
410 ]);
411 $tokens = $tokenProcessor->listTokens();
412 // Add in custom tokens as token processor supports these.
413 $expectedTokens['{membership.custom_1}'] = 'Enter text here :: Group with field text';
414 $this->assertEquals(array_merge($expectedTokens, $this->getDomainTokens()), $tokens);
415 $tokenProcessor->addMessage('html', $tokenString, 'text/plain');
416 $tokenProcessor->addRow(['membershipId' => $this->getMembershipID()]);
417 $tokenProcessor->evaluate();
418 $this->assertEquals($expected, $tokenProcessor->getRow(0)->render('html'));
419
420 }
421
422 /**
423 * Get declared membership tokens.
424 *
425 * @return string[]
426 */
427 public function getMembershipTokens(): array {
428 return [
429 '{membership.id}' => 'Membership ID',
430 '{membership.status_id:label}' => 'Membership Status',
431 '{membership.membership_type_id:label}' => 'Membership Type',
432 '{membership.start_date}' => 'Membership Start Date',
433 '{membership.join_date}' => 'Membership Join Date',
434 '{membership.end_date}' => 'Membership End Date',
435 '{membership.fee}' => 'Membership Fee',
436 ];
437 }
438
439 /**
440 * Get case ID.
441 *
442 * @return int
443 */
444 protected function getMembershipID(): int {
445 if (!isset($this->ids['Membership'][0])) {
446 $this->ids['Membership'][0] = $this->contactMembershipCreate([
447 'contact_id' => $this->getContactID(),
448 $this->getCustomFieldName('text') => 'my field',
449 ]);
450 }
451 return $this->ids['Membership'][0];
452 }
453
454 /**
455 * Get expected output from token parsing.
456 *
457 * @return string
458 */
459 protected function getExpectedEventTokenOutput(): string {
460 return '
461 1
462 Annual CiviCRM meet
463 October 21st, 2008
464 October 23rd, 2008
465 Conference
466 If you have any CiviCRM related issues or want to track where CiviCRM is heading, Sign up now
467 event@example.com
468 456 789
469 event description
470 15 Walton St
471 Emerald City, Maine 90210
472
473 $ 50.00
474 ' . CRM_Utils_System::url('civicrm/event/info', NULL, TRUE) . '&reset=1&id=1
475 ' . CRM_Utils_System::url('civicrm/event/register', NULL, TRUE) . '&reset=1&id=1
476
477 my field';
478 }
479
480 /**
481 * Get expected output from token parsing.
482 *
483 * @return string
484 */
485 protected function getExpectedMembershipTokenOutput(): string {
486 return '
487 Expired
488 General
489 1
490 Expired
491 General
492 January 21st, 2007
493 January 21st, 2007
494 December 21st, 2007
495 100.00';
496 }
497
498 /**
499 * Test that membership tokens are consistently rendered.
500 *
501 * @throws \API_Exception
502 */
503 public function testParticipantTokenConsistency(): void {
504 $this->createLoggedInUser();
505 $this->createCustomGroupWithFieldOfType(['extends' => 'Participant']);
506 $tokens = CRM_Core_SelectValues::participantTokens();
507 $this->assertEquals($this->getParticipantTokens(), $tokens);
508 }
509
510 /**
511 * Get declared participant tokens.
512 *
513 * @return string[]
514 */
515 public function getParticipantTokens(): array {
516 return [
517 '{participant.status_id}' => 'Status ID',
518 '{participant.role_id}' => 'Participant Role (ID)',
519 '{participant.register_date}' => 'Register date',
520 '{participant.source}' => 'Participant Source',
521 '{participant.fee_level}' => 'Fee level',
522 '{participant.fee_amount}' => 'Fee Amount',
523 '{participant.registered_by_id}' => 'Registered By Participant ID',
524 '{participant.transferred_to_contact_id}' => 'Transferred to Contact ID',
525 '{participant.role_id:label}' => 'Participant Role (label)',
526 '{participant.fee_label}' => 'Fee Label',
527 '{participant.' . $this->getCustomFieldName('text') . '}' => 'Enter text here :: Group with field text',
528 ];
529 }
530
531 /**
532 * Test that domain tokens are consistently rendered.
533 */
534 public function testDomainTokenConsistency(): void {
535 $tokens = CRM_Core_SelectValues::domainTokens();
536 $this->assertEquals($this->getDomainTokens(), $tokens);
537 $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
538 'controller' => __CLASS__,
539 'smarty' => FALSE,
540 ]);
541 $tokens['{domain.id}'] = 'Domain ID';
542 $tokens['{domain.description}'] = 'Domain Description';
543 $tokens['{domain.now}'] = 'Current time/date';
544 $this->assertEquals($tokens, $tokenProcessor->listTokens());
545 }
546
547 /**
548 * @throws \API_Exception
549 * @throws \CRM_Core_Exception
550 */
551 public function testDomainNow(): void {
552 putenv('TIME_FUNC=frozen');
553 CRM_Utils_Time::setTime('2021-09-18 23:58:00');
554 $modifiers = [
555 'shortdate' => '09/18/2021',
556 '%B %Y' => 'September 2021',
557 ];
558 foreach ($modifiers as $filter => $expected) {
559 $resolved = CRM_Core_BAO_MessageTemplate::renderTemplate([
560 'messageTemplate' => [
561 'msg_text' => '{domain.now|crmDate:"' . $filter . '"}',
562 ],
563 ])['text'];
564 $this->assertEquals($expected, $resolved);
565 }
566 $resolved = CRM_Core_BAO_MessageTemplate::renderTemplate([
567 'messageTemplate' => [
568 'msg_text' => '{domain.now}',
569 ],
570 ])['text'];
571 $this->assertEquals('September 18th, 2021 11:58 PM', $resolved);
572
573 // This example is malformed - no quotes
574 try {
575 $resolved = CRM_Core_BAO_MessageTemplate::renderTemplate([
576 'messageTemplate' => [
577 'msg_text' => '{domain.now|crmDate:shortdate}',
578 ],
579 ])['text'];
580 $this->fail("Expected unquoted parameter to fail");
581 }
582 catch (\CRM_Core_Exception $e) {
583 $this->assertRegExp(';Malformed token param;', $e->getMessage());
584 }
585 }
586
587 /**
588 * Get declared participant tokens.
589 *
590 * @return string[]
591 */
592 public function getDomainTokens(): array {
593 return [
594 '{domain.name}' => ts('Domain name'),
595 '{domain.address}' => ts('Domain (organization) address'),
596 '{domain.phone}' => ts('Domain (organization) phone'),
597 '{domain.email}' => 'Domain (organization) email',
598 '{domain.id}' => ts('Domain ID'),
599 '{domain.description}' => ts('Domain Description'),
600 '{domain.now}' => 'Current time/date',
601 ];
602 }
603
604 /**
605 * Test that domain tokens are consistently rendered.
606 *
607 * @throws \API_Exception
608 */
609 public function testEventTokenConsistency(): void {
610 $mut = new CiviMailUtils($this);
611 $this->setupParticipantScheduledReminder();
612
613 $tokens = CRM_Core_SelectValues::eventTokens();
614 $this->assertEquals($this->getEventTokens(), $tokens);
615 $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
616 'controller' => __CLASS__,
617 'smarty' => FALSE,
618 'schema' => ['eventId'],
619 ]);
620 $this->assertEquals(array_merge($tokens, $this->getDomainTokens()), $tokenProcessor->listTokens());
621
622 $this->callAPISuccess('job', 'send_reminder', []);
623 $expected = $this->getExpectedEventTokenOutput();
624 $mut->checkMailLog([$expected]);
625 }
626
627 /**
628 * Set up scheduled reminder for participants.
629 *
630 * @throws \API_Exception
631 */
632 public function setupParticipantScheduledReminder(): void {
633 $this->createCustomGroupWithFieldOfType(['extends' => 'Event']);
634 $emailID = Email::create()->setValues(['email' => 'event@example.com'])->execute()->first()['id'];
635 $addressID = Address::create()->setValues([
636 'street_address' => '15 Walton St',
637 'supplemental_address_1' => 'up the road',
638 'city' => 'Emerald City',
639 'state_province_id:label' => 'Maine',
640 'postal_code' => 90210,
641 ])->execute()->first()['id'];
642 $phoneID = Phone::create()->setValues(['phone' => '456 789'])->execute()->first()['id'];
643
644 $locationBlockID = LocBlock::save(FALSE)->setRecords([
645 [
646 'email_id' => $emailID,
647 'address_id' => $addressID,
648 'phone_id' => $phoneID,
649 ],
650 ])->execute()->first()['id'];
651 $event = $this->eventCreate([
652 'description' => 'event description',
653 $this->getCustomFieldName('text') => 'my field',
654 'loc_block_id' => $locationBlockID,
655 ]);
656 // Create an unrelated participant record so that the ids don't match.
657 // this prevents things working just because the id 'happens to be valid'
658 $this->participantCreate(['register_date' => '2020-01-01', 'event_id' => $event['id']]);
659 $this->participantCreate(['event_id' => $event['id'], 'fee_amount' => 50]);
660 CRM_Utils_Time::setTime('2007-02-20 15:00:00');
661 $this->callAPISuccess('action_schedule', 'create', [
662 'title' => 'job',
663 'subject' => 'job',
664 'entity_value' => 1,
665 'mapping_id' => 2,
666 'start_action_date' => 'register_date',
667 'start_action_offset' => 1,
668 'start_action_condition' => 'after',
669 'start_action_unit' => 'day',
670 'body_html' => implode("\n", array_keys($this->getEventTokens())),
671 ]);
672 }
673
674 /**
675 * Get expected event tokens.
676 *
677 * @return string[]
678 */
679 protected function getEventTokens(): array {
680 return [
681 '{event.event_id}' => 'Event ID',
682 '{event.title}' => 'Event Title',
683 '{event.start_date}' => 'Event Start Date',
684 '{event.end_date}' => 'Event End Date',
685 '{event.event_type}' => 'Event Type',
686 '{event.summary}' => 'Event Summary',
687 '{event.contact_email}' => 'Event Contact Email',
688 '{event.contact_phone}' => 'Event Contact Phone',
689 '{event.description}' => 'Event Description',
690 '{event.location}' => 'Event Location',
691 '{event.fee_amount}' => 'Event Fee',
692 '{event.info_url}' => 'Event Info URL',
693 '{event.registration_url}' => 'Event Registration URL',
694 '{event.balance}' => 'Event Balance',
695 '{event.' . $this->getCustomFieldName('text') . '}' => 'Enter text here :: Group with field text',
696 ];
697 }
698
699 }