Merge pull request #21647 from colemanw/coalesce
[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 12:00 AM
464 October 23rd, 2008 12:00 AM
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) . '&amp;reset=1&amp;id=1
475 ' . CRM_Utils_System::url('civicrm/event/register', NULL, TRUE) . '&amp;reset=1&amp;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 }