Commit | Line | Data |
---|---|---|
d0ce76fd EM |
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 | ||
0f4031da | 12 | use Civi\Token\TokenProcessor; |
f399fbd8 EM |
13 | use Civi\Api4\LocBlock; |
14 | use Civi\Api4\Email; | |
15 | use Civi\Api4\Phone; | |
16 | use Civi\Api4\Address; | |
0f4031da | 17 | |
d0ce76fd EM |
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 | ||
0f4031da EM |
38 | /** |
39 | * Recurring contribution. | |
40 | * | |
41 | * @var array | |
42 | */ | |
43 | protected $contributionRecur; | |
44 | ||
d0ce76fd EM |
45 | /** |
46 | * Post test cleanup. | |
d0ce76fd EM |
47 | */ |
48 | public function tearDown(): void { | |
e80f2261 | 49 | $this->quickCleanup(['civicrm_case', 'civicrm_case_type', 'civicrm_participant', 'civicrm_event'], TRUE); |
f70a513f | 50 | $this->quickCleanUpFinancialEntities(); |
167f848f TO |
51 | |
52 | // WORKAROUND: CRM_Event_Tokens copies `civicrm_event` data into metadata cache. That should probably change, but that's a different scope-of-work. | |
53 | // `clear()` works around it. This should be removed if that's updated, but it will be safe either way. | |
54 | Civi::cache('metadata')->clear(); | |
55 | ||
d0ce76fd EM |
56 | parent::tearDown(); |
57 | } | |
58 | ||
59 | /** | |
60 | * Test that case tokens are consistently rendered. | |
61 | * | |
d0ce76fd EM |
62 | * @throws \CiviCRM_API3_Exception |
63 | */ | |
64 | public function testCaseTokenConsistency(): void { | |
65 | $this->createLoggedInUser(); | |
66 | CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase'); | |
67 | $this->createCustomGroupWithFieldOfType(['extends' => 'Case']); | |
68 | $tokens = CRM_Core_SelectValues::caseTokens(); | |
69 | $this->assertEquals($this->getCaseTokens(), $tokens); | |
70 | $caseID = $this->getCaseID(); | |
044c0ad1 | 71 | $tokenString = $this->getTokenString(array_keys($this->getCaseTokens())); |
78ffc4d7 | 72 | $tokenHtml = CRM_Utils_Token::replaceCaseTokens($caseID, $tokenString, ['case' => $this->getCaseTokenKeys()]); |
d0ce76fd EM |
73 | $this->assertEquals($this->getExpectedCaseTokenOutput(), $tokenHtml); |
74 | // Now do the same without passing in 'knownTokens' | |
78ffc4d7 | 75 | $tokenHtml = CRM_Utils_Token::replaceCaseTokens($caseID, $tokenString); |
d0ce76fd | 76 | $this->assertEquals($this->getExpectedCaseTokenOutput(), $tokenHtml); |
2f5c0408 EM |
77 | |
78 | // And check our deprecated tokens still work. | |
79 | $tokenHtml = CRM_Utils_Token::replaceCaseTokens($caseID, '{case.case_type_id} {case.status_id}'); | |
80 | $this->assertEquals('Housing Support Ongoing', $tokenHtml); | |
78ffc4d7 EM |
81 | $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [ |
82 | 'controller' => __CLASS__, | |
83 | 'smarty' => FALSE, | |
84 | 'schema' => ['caseId'], | |
85 | ]); | |
889b0617 | 86 | $this->assertEquals(array_merge($this->getCaseTokens(), $this->getDomainTokens()), $tokenProcessor->listTokens()); |
78ffc4d7 EM |
87 | $tokenProcessor->addRow([ |
88 | 'caseId' => $this->getCaseID(), | |
89 | ]); | |
90 | $tokenProcessor->addMessage('html', $tokenString, 'text/plain'); | |
91 | ||
92 | $tokenProcessor->evaluate(); | |
93 | foreach ($tokenProcessor->getRows() as $row) { | |
94 | $text = $row->render('html'); | |
95 | } | |
96 | $this->assertEquals($this->getExpectedCaseTokenOutput(), $text); | |
d0ce76fd EM |
97 | } |
98 | ||
99 | /** | |
100 | * Get expected output from token parsing. | |
101 | * | |
102 | * @return string | |
103 | */ | |
104 | protected function getExpectedCaseTokenOutput(): string { | |
044c0ad1 EM |
105 | return 'case.id :1 |
106 | case.case_type_id:label :Housing Support | |
107 | case.subject :Case Subject | |
108 | case.start_date :July 23rd, 2021 | |
109 | case.end_date :July 26th, 2021 | |
110 | case.details :case details | |
111 | case.status_id:label :Ongoing | |
112 | case.is_deleted:label :No | |
113 | case.created_date :' . CRM_Utils_Date::customFormat($this->case['created_date']) . ' | |
114 | case.modified_date :' . CRM_Utils_Date::customFormat($this->case['modified_date']) . ' | |
115 | case.custom_1 :' . ' | |
d0ce76fd EM |
116 | '; |
117 | } | |
118 | ||
119 | /** | |
120 | * @return int | |
121 | */ | |
122 | protected function getContactID(): int { | |
123 | if (!isset($this->ids['Contact'][0])) { | |
124 | $this->ids['Contact'][0] = $this->individualCreate(); | |
125 | } | |
126 | return $this->ids['Contact'][0]; | |
127 | } | |
128 | ||
129 | /** | |
130 | * Get the keys for the case tokens. | |
131 | * | |
132 | * @return array | |
133 | */ | |
134 | public function getCaseTokenKeys(): array { | |
135 | $return = []; | |
136 | foreach (array_keys($this->getCaseTokens()) as $key) { | |
137 | $return[] = substr($key, 6, -1); | |
138 | } | |
139 | return $return; | |
140 | } | |
141 | ||
142 | /** | |
143 | * Get declared tokens. | |
144 | * | |
145 | * @return string[] | |
146 | */ | |
147 | public function getCaseTokens(): array { | |
148 | return [ | |
149 | '{case.id}' => 'Case ID', | |
2f5c0408 | 150 | '{case.case_type_id:label}' => 'Case Type', |
d0ce76fd EM |
151 | '{case.subject}' => 'Case Subject', |
152 | '{case.start_date}' => 'Case Start Date', | |
153 | '{case.end_date}' => 'Case End Date', | |
154 | '{case.details}' => 'Details', | |
2f5c0408 | 155 | '{case.status_id:label}' => 'Case Status', |
78ffc4d7 | 156 | '{case.is_deleted:label}' => 'Case is in the Trash', |
d0ce76fd EM |
157 | '{case.created_date}' => 'Created Date', |
158 | '{case.modified_date}' => 'Modified Date', | |
159 | '{case.custom_1}' => 'Enter text here :: Group with field text', | |
160 | ]; | |
161 | } | |
162 | ||
163 | /** | |
164 | * Get case ID. | |
165 | * | |
166 | * @return int | |
167 | */ | |
168 | protected function getCaseID(): int { | |
169 | if (!isset($this->case)) { | |
a0fb3068 | 170 | $case_id = $this->callAPISuccess('Case', 'create', [ |
d0ce76fd EM |
171 | 'case_type_id' => 'housing_support', |
172 | 'activity_subject' => 'Case Subject', | |
173 | 'client_id' => $this->getContactID(), | |
174 | 'status_id' => 1, | |
175 | 'subject' => 'Case Subject', | |
176 | 'start_date' => '2021-07-23 15:39:20', | |
a0fb3068 | 177 | // Note end_date is inconsistent with status Ongoing but for the |
178 | // purposes of testing tokens is ok. Creating it with status Resolved | |
179 | // then ignores our known fixed end date. | |
d0ce76fd EM |
180 | 'end_date' => '2021-07-26 18:07:20', |
181 | 'medium_id' => 2, | |
182 | 'details' => 'case details', | |
183 | 'activity_details' => 'blah blah', | |
184 | 'sequential' => 1, | |
a0fb3068 | 185 | ])['id']; |
186 | // Need to retrieve the case again because modified date might be updated a | |
187 | // split-second later than the original return value because of activity | |
188 | // triggers when the timeline is populated. The returned array from create | |
189 | // is determined before that happens. | |
190 | $this->case = $this->callAPISuccess('Case', 'getsingle', ['id' => $case_id]); | |
d0ce76fd EM |
191 | } |
192 | return $this->case['id']; | |
193 | } | |
194 | ||
0f4031da EM |
195 | /** |
196 | * Test that contribution recur tokens are consistently rendered. | |
197 | */ | |
198 | public function testContributionRecurTokenConsistency(): void { | |
199 | $this->createLoggedInUser(); | |
200 | $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [ | |
201 | 'controller' => __CLASS__, | |
202 | 'smarty' => FALSE, | |
203 | 'schema' => ['contribution_recurId'], | |
204 | ]); | |
889b0617 EM |
205 | $expectedTokens = array_merge($this->getContributionRecurTokens(), $this->getDomainTokens()); |
206 | $this->assertEquals(array_diff_key($expectedTokens, $this->getUnadvertisedTokens()), $tokenProcessor->listTokens()); | |
044c0ad1 | 207 | $tokenString = $this->getTokenString(array_keys($this->getContributionRecurTokens())); |
0f4031da EM |
208 | |
209 | $tokenProcessor->addMessage('html', $tokenString, 'text/plain'); | |
210 | $tokenProcessor->addRow(['contribution_recurId' => $this->getContributionRecurID()]); | |
211 | $tokenProcessor->evaluate(); | |
212 | $this->assertEquals($this->getExpectedContributionRecurTokenOutPut(), $tokenProcessor->getRow(0)->render('html')); | |
213 | } | |
214 | ||
b0a68d88 EM |
215 | /** |
216 | * Test that contribution recur tokens are consistently rendered. | |
217 | */ | |
218 | public function testContributionRecurTokenRaw(): void { | |
219 | $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [ | |
220 | 'controller' => __CLASS__, | |
221 | 'smarty' => FALSE, | |
222 | 'schema' => ['contribution_recurId'], | |
223 | ]); | |
224 | $tokenProcessor->addMessage('not_specified', '{contribution_recur.amount}', 'text/plain'); | |
225 | $tokenProcessor->addMessage('money', '{contribution_recur.amount|crmMoney}', 'text/plain'); | |
226 | $tokenProcessor->addMessage('raw', '{contribution_recur.amount|raw}', 'text/plain'); | |
227 | $tokenProcessor->addMessage('moneyNumber', '{contribution_recur.amount|crmMoneyNumber}', 'text/plain'); | |
228 | $tokenProcessor->addRow(['contribution_recurId' => $this->getContributionRecurID()]); | |
229 | $tokenProcessor->evaluate(); | |
230 | $this->assertEquals('€5,990.99', $tokenProcessor->getRow(0)->render('not_specified')); | |
231 | $this->assertEquals('€5,990.99', $tokenProcessor->getRow(0)->render('money')); | |
232 | $this->assertEquals('5990.99', $tokenProcessor->getRow(0)->render('raw')); | |
233 | } | |
234 | ||
f70a513f EM |
235 | /** |
236 | * Test money format tokens can respect passed in locale. | |
237 | */ | |
238 | public function testMoneyFormat(): void { | |
239 | // Our 'migration' off configured thousand separators at the moment is a define. | |
240 | putenv('IGNORE_SEPARATOR_CONFIG=1'); | |
241 | $this->createLoggedInUser(); | |
242 | $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [ | |
243 | 'controller' => __CLASS__, | |
244 | 'smarty' => FALSE, | |
245 | 'schema' => ['contribution_recurId'], | |
246 | ]); | |
247 | $tokenString = '{contribution_recur.amount}'; | |
248 | $tokenProcessor->addMessage('html', $tokenString, 'text/plain'); | |
249 | $tokenProcessor->addRow([ | |
250 | 'contribution_recurId' => $this->getContributionRecurID(), | |
251 | 'locale' => 'nb_NO', | |
252 | ]); | |
253 | $tokenProcessor->evaluate(); | |
254 | $this->assertEquals('€ 5 990,99', $tokenProcessor->getRow(0)->render('html')); | |
255 | } | |
256 | ||
e9841a51 EM |
257 | /** |
258 | * Get tokens that are not advertised via listTokens. | |
259 | * | |
260 | * @return string[] | |
261 | */ | |
262 | public function getUnadvertisedTokens(): array { | |
263 | return [ | |
264 | '{membership.status_id}' => 'Status ID', | |
265 | '{membership.membership_type_id}' => 'Membership Type ID', | |
266 | '{membership.status_id:name}' => 'Machine name: Status', | |
267 | '{membership.membership_type_id:name}' => 'Machine name: Membership Type', | |
889b0617 EM |
268 | '{contribution_recur.frequency_unit}' => 'Frequency Unit', |
269 | '{contribution_recur.contribution_status_id}' => 'Status', | |
270 | '{contribution_recur.payment_processor_id}' => 'Payment Processor ID', | |
271 | '{contribution_recur.financial_type_id}' => 'Financial Type ID', | |
272 | '{contribution_recur.payment_instrument_id}' => 'Payment Method', | |
273 | '{contribution_recur.frequency_unit:name}' => 'Machine name: Frequency Unit', | |
274 | '{contribution_recur.payment_instrument_id:name}' => 'Machine name: Payment Method', | |
275 | '{contribution_recur.contribution_status_id:name}' => 'Machine name: Status', | |
276 | '{contribution_recur.payment_processor_id:name}' => 'Machine name: Payment Processor', | |
277 | '{contribution_recur.financial_type_id:name}' => 'Machine name: Financial Type', | |
278 | '{participant.status_id:name}' => 'Machine name: Status', | |
279 | '{participant.role_id:name}' => 'Machine name: Participant Role', | |
280 | '{participant.status_id}' => 'Status ID', | |
281 | '{participant.role_id}' => 'Participant Role ID', | |
e9841a51 EM |
282 | ]; |
283 | } | |
284 | ||
d49e8eec EM |
285 | /** |
286 | * Test tokens in 2 ways to ensure consistent handling. | |
287 | * | |
288 | * 1) as part of the greeting processing | |
289 | * 2) via the token processor. | |
290 | * | |
291 | */ | |
292 | public function testOddTokens(): void { | |
293 | ||
294 | $variants = [ | |
295 | [ | |
296 | 'string' => '{contact.individual_prefix}{ }{contact.first_name}{ }{contact.middle_name}{ }{contact.last_name}{ }{contact.individual_suffix}', | |
297 | 'expected' => 'Mr. Anthony Anderson II', | |
298 | ], | |
299 | [ | |
300 | 'string' => '{contact.prefix_id:label}{ }{contact.first_name}{ }{contact.middle_name}{ }{contact.last_name}{ }{contact.suffix_id:label}', | |
301 | 'expected' => 'Mr. Anthony Anderson II', | |
302 | ], | |
303 | ]; | |
304 | $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [ | |
305 | 'smarty' => FALSE, | |
306 | 'schema' => ['contactId'], | |
307 | ]); | |
308 | $contactID = $this->individualCreate(['middle_name' => '']); | |
309 | $tokenProcessor->addRow(['contactId' => $contactID]); | |
d49e8eec EM |
310 | foreach ($variants as $index => $variant) { |
311 | $tokenProcessor->addMessage($index, $variant['string'], 'text/plain'); | |
312 | } | |
313 | $tokenProcessor->evaluate(); | |
314 | $result = $tokenProcessor->getRow(0); | |
315 | foreach ($variants as $index => $variant) { | |
316 | $greetingString = $variant['string']; | |
317 | CRM_Utils_Token::replaceGreetingTokens($greetingString, $this->callAPISuccessGetSingle('Contact', ['id' => $contactID]), $contactID); | |
0bb18246 TO |
318 | $this->assertEquals($variant['expected'], $greetingString, 'replaceGreetingTokens() should render expected output'); |
319 | $this->assertEquals($variant['expected'], $result->render($index), 'TokenProcessor should render expected output'); | |
d49e8eec EM |
320 | } |
321 | } | |
322 | ||
0f4031da EM |
323 | /** |
324 | * Get the contribution recur tokens keyed by the token. | |
325 | * | |
326 | * e.g {contribution_recur.id} | |
327 | * | |
328 | * @return array | |
329 | */ | |
330 | protected function getContributionRecurTokens(): array { | |
331 | $return = []; | |
332 | foreach ($this->getContributionRecurTokensByField() as $key => $value) { | |
333 | $return['{contribution_recur.' . $key . '}'] = $value; | |
334 | } | |
335 | return $return; | |
336 | } | |
337 | ||
338 | protected function getContributionRecurTokensByField(): array { | |
339 | return [ | |
340 | 'id' => 'Recurring Contribution ID', | |
341 | 'amount' => 'Amount', | |
342 | 'currency' => 'Currency', | |
343 | 'frequency_unit' => 'Frequency Unit', | |
344 | 'frequency_interval' => 'Interval (number of units)', | |
345 | 'installments' => 'Number of Installments', | |
346 | 'start_date' => 'Start Date', | |
347 | 'create_date' => 'Created Date', | |
348 | 'modified_date' => 'Modified Date', | |
349 | 'cancel_date' => 'Cancel Date', | |
350 | 'cancel_reason' => 'Cancellation Reason', | |
351 | 'end_date' => 'Recurring Contribution End Date', | |
352 | 'processor_id' => 'Processor ID', | |
353 | 'payment_token_id' => 'Payment Token ID', | |
354 | 'trxn_id' => 'Transaction ID', | |
355 | 'invoice_id' => 'Invoice ID', | |
356 | 'contribution_status_id' => 'Status', | |
97ca72e4 | 357 | 'is_test:label' => 'Test', |
0f4031da EM |
358 | 'cycle_day' => 'Cycle Day', |
359 | 'next_sched_contribution_date' => 'Next Scheduled Contribution Date', | |
360 | 'failure_count' => 'Number of Failures', | |
361 | 'failure_retry_date' => 'Retry Failed Attempt Date', | |
97ca72e4 | 362 | 'auto_renew:label' => 'Auto Renew', |
0f4031da EM |
363 | 'payment_processor_id' => 'Payment Processor ID', |
364 | 'financial_type_id' => 'Financial Type ID', | |
365 | 'payment_instrument_id' => 'Payment Method', | |
97ca72e4 | 366 | 'is_email_receipt:label' => 'Send email Receipt?', |
0f4031da EM |
367 | 'frequency_unit:label' => 'Frequency Unit', |
368 | 'frequency_unit:name' => 'Machine name: Frequency Unit', | |
369 | 'contribution_status_id:label' => 'Status', | |
370 | 'contribution_status_id:name' => 'Machine name: Status', | |
371 | 'payment_processor_id:label' => 'Payment Processor', | |
372 | 'payment_processor_id:name' => 'Machine name: Payment Processor', | |
373 | 'financial_type_id:label' => 'Financial Type', | |
374 | 'financial_type_id:name' => 'Machine name: Financial Type', | |
375 | 'payment_instrument_id:label' => 'Payment Method', | |
376 | 'payment_instrument_id:name' => 'Machine name: Payment Method', | |
377 | ]; | |
378 | } | |
379 | ||
380 | /** | |
381 | * Get contributionRecur ID. | |
382 | * | |
383 | * @return int | |
384 | */ | |
385 | protected function getContributionRecurID(): int { | |
386 | if (!isset($this->contributionRecur)) { | |
387 | $paymentProcessorID = $this->processorCreate(); | |
388 | $this->contributionRecur = $this->callAPISuccess('ContributionRecur', 'create', [ | |
389 | 'contact_id' => $this->getContactID(), | |
390 | 'status_id' => 1, | |
391 | 'is_email_receipt' => 1, | |
392 | 'start_date' => '2021-07-23 15:39:20', | |
393 | 'end_date' => '2021-07-26 18:07:20', | |
394 | 'cancel_date' => '2021-08-19 09:12:45', | |
34795e7a | 395 | 'next_sched_contribution_date' => '2021-09-08', |
0f4031da EM |
396 | 'cancel_reason' => 'Because', |
397 | 'amount' => 5990.99, | |
398 | 'currency' => 'EUR', | |
399 | 'frequency_unit' => 'year', | |
400 | 'frequency_interval' => 2, | |
401 | 'installments' => 24, | |
402 | 'payment_instrument_id' => 'Check', | |
403 | 'financial_type_id' => 'Member dues', | |
404 | 'processor_id' => 'abc', | |
405 | 'payment_processor_id' => $paymentProcessorID, | |
406 | 'trxn_id' => 123, | |
407 | 'invoice_id' => 'inv123', | |
408 | 'sequential' => 1, | |
409 | 'failure_retry_date' => '2020-01-03', | |
410 | 'auto_renew' => 1, | |
411 | 'cycle_day' => '15', | |
412 | 'is_test' => TRUE, | |
413 | 'payment_token_id' => $this->callAPISuccess('PaymentToken', 'create', [ | |
414 | 'contact_id' => $this->getContactID(), | |
415 | 'token' => 456, | |
416 | 'payment_processor_id' => $paymentProcessorID, | |
417 | ])['id'], | |
418 | ])['values'][0]; | |
419 | } | |
420 | return $this->contributionRecur['id']; | |
421 | } | |
422 | ||
423 | /** | |
424 | * Get rendered output for contribution tokens. | |
425 | * | |
426 | * @return string | |
427 | */ | |
428 | protected function getExpectedContributionRecurTokenOutPut(): string { | |
044c0ad1 | 429 | return 'contribution_recur.id :' . $this->getContributionRecurID() . ' |
f70a513f | 430 | contribution_recur.amount :€5,990.99 |
044c0ad1 EM |
431 | contribution_recur.currency :EUR |
432 | contribution_recur.frequency_unit :year | |
433 | contribution_recur.frequency_interval :2 | |
434 | contribution_recur.installments :24 | |
435 | contribution_recur.start_date :July 23rd, 2021 3:39 PM | |
436 | contribution_recur.create_date :' . CRM_Utils_Date::customFormat($this->contributionRecur['create_date']) . ' | |
437 | contribution_recur.modified_date :' . CRM_Utils_Date::customFormat($this->contributionRecur['modified_date']) . ' | |
438 | contribution_recur.cancel_date :August 19th, 2021 9:12 AM | |
439 | contribution_recur.cancel_reason :Because | |
440 | contribution_recur.end_date :July 26th, 2021 6:07 PM | |
441 | contribution_recur.processor_id :abc | |
442 | contribution_recur.payment_token_id :1 | |
443 | contribution_recur.trxn_id :123 | |
444 | contribution_recur.invoice_id :inv123 | |
445 | contribution_recur.contribution_status_id :2 | |
446 | contribution_recur.is_test:label :Yes | |
447 | contribution_recur.cycle_day :15 | |
448 | contribution_recur.next_sched_contribution_date :September 8th, 2021 | |
449 | contribution_recur.failure_count :0 | |
450 | contribution_recur.failure_retry_date :January 3rd, 2020 | |
451 | contribution_recur.auto_renew:label :Yes | |
452 | contribution_recur.payment_processor_id :1 | |
453 | contribution_recur.financial_type_id :2 | |
454 | contribution_recur.payment_instrument_id :4 | |
455 | contribution_recur.is_email_receipt:label :Yes | |
456 | contribution_recur.frequency_unit:label :year | |
457 | contribution_recur.frequency_unit:name :year | |
458 | contribution_recur.contribution_status_id:label :Pending Label** | |
459 | contribution_recur.contribution_status_id:name :Pending | |
460 | contribution_recur.payment_processor_id:label :Dummy (test) | |
202723ae | 461 | contribution_recur.payment_processor_id:name :Dummy_test |
044c0ad1 EM |
462 | contribution_recur.financial_type_id:label :Member Dues |
463 | contribution_recur.financial_type_id:name :Member Dues | |
464 | contribution_recur.payment_instrument_id:label :Check | |
465 | contribution_recur.payment_instrument_id:name :Check | |
466 | '; | |
467 | ||
0f4031da EM |
468 | } |
469 | ||
d41a5d53 EM |
470 | /** |
471 | * Test that membership tokens are consistently rendered. | |
472 | * | |
473 | * @throws \API_Exception | |
474 | */ | |
475 | public function testMembershipTokenConsistency(): void { | |
476 | $this->createLoggedInUser(); | |
477 | $this->restoreMembershipTypes(); | |
478 | $this->createCustomGroupWithFieldOfType(['extends' => 'Membership']); | |
479 | $tokens = CRM_Core_SelectValues::membershipTokens(); | |
d568dbe0 EM |
480 | $expectedTokens = $this->getMembershipTokens(); |
481 | $this->assertEquals($expectedTokens, $tokens); | |
d41a5d53 EM |
482 | $newStyleTokens = "\n{membership.status_id:label}\n{membership.membership_type_id:label}\n"; |
483 | $tokenString = $newStyleTokens . implode("\n", array_keys($this->getMembershipTokens())); | |
b024d6a1 | 484 | |
d41a5d53 EM |
485 | $memberships = CRM_Utils_Token::getMembershipTokenDetails([$this->getMembershipID()]); |
486 | $messageToken = CRM_Utils_Token::getTokens($tokenString); | |
487 | $tokenHtml = CRM_Utils_Token::replaceEntityTokens('membership', $memberships[$this->getMembershipID()], $tokenString, $messageToken); | |
488 | $this->assertEquals($this->getExpectedMembershipTokenOutput(), $tokenHtml); | |
dd2f879a | 489 | |
b024d6a1 EM |
490 | // Custom fields work in the processor so test it.... |
491 | $tokenString .= "\n{membership." . $this->getCustomFieldName('text') . '}'; | |
dd2f879a EM |
492 | // Now compare with scheduled reminder |
493 | $mut = new CiviMailUtils($this); | |
494 | CRM_Utils_Time::setTime('2007-01-22 15:00:00'); | |
495 | $this->callAPISuccess('action_schedule', 'create', [ | |
496 | 'title' => 'job', | |
497 | 'subject' => 'job', | |
498 | 'entity_value' => 1, | |
499 | 'mapping_id' => 4, | |
500 | 'start_action_date' => 'membership_join_date', | |
501 | 'start_action_offset' => 1, | |
502 | 'start_action_condition' => 'after', | |
503 | 'start_action_unit' => 'day', | |
504 | 'body_html' => $tokenString, | |
505 | ]); | |
506 | $this->callAPISuccess('job', 'send_reminder', []); | |
d568dbe0 EM |
507 | $expected = $this->getExpectedMembershipTokenOutput(); |
508 | // Unlike the legacy method custom fields are resolved by the processor. | |
509 | $expected .= "\nmy field"; | |
510 | $mut->checkMailLog([$expected]); | |
511 | ||
512 | $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [ | |
513 | 'controller' => __CLASS__, | |
514 | 'smarty' => FALSE, | |
515 | 'schema' => ['membershipId'], | |
516 | ]); | |
517 | $tokens = $tokenProcessor->listTokens(); | |
518 | // Add in custom tokens as token processor supports these. | |
889b0617 | 519 | $expectedTokens = array_merge($expectedTokens, $this->getTokensAdvertisedByTokenProcessorButNotLegacy()); |
3c78698e | 520 | $this->assertEquals(array_merge($expectedTokens, $this->getDomainTokens()), $tokens); |
d568dbe0 EM |
521 | $tokenProcessor->addMessage('html', $tokenString, 'text/plain'); |
522 | $tokenProcessor->addRow(['membershipId' => $this->getMembershipID()]); | |
523 | $tokenProcessor->evaluate(); | |
524 | $this->assertEquals($expected, $tokenProcessor->getRow(0)->render('html')); | |
525 | ||
d41a5d53 EM |
526 | } |
527 | ||
889b0617 EM |
528 | /** |
529 | * Get the advertised tokens the legacy function doesn't know about. | |
530 | * | |
531 | * @return string[] | |
532 | */ | |
533 | public function getTokensAdvertisedByTokenProcessorButNotLegacy(): array { | |
534 | return [ | |
535 | '{membership.custom_1}' => 'Enter text here :: Group with field text', | |
536 | '{membership.source}' => 'Source', | |
537 | '{membership.status_override_end_date}' => 'Status Override End Date', | |
538 | ]; | |
539 | } | |
540 | ||
d41a5d53 EM |
541 | /** |
542 | * Get declared membership tokens. | |
543 | * | |
544 | * @return string[] | |
545 | */ | |
546 | public function getMembershipTokens(): array { | |
547 | return [ | |
548 | '{membership.id}' => 'Membership ID', | |
044c0ad1 | 549 | '{membership.status_id:label}' => 'Status', |
eac0a5bf | 550 | '{membership.membership_type_id:label}' => 'Membership Type', |
d41a5d53 | 551 | '{membership.start_date}' => 'Membership Start Date', |
044c0ad1 EM |
552 | '{membership.join_date}' => 'Member Since', |
553 | '{membership.end_date}' => 'Membership Expiration Date', | |
d41a5d53 EM |
554 | '{membership.fee}' => 'Membership Fee', |
555 | ]; | |
556 | } | |
557 | ||
558 | /** | |
559 | * Get case ID. | |
560 | * | |
561 | * @return int | |
562 | */ | |
563 | protected function getMembershipID(): int { | |
564 | if (!isset($this->ids['Membership'][0])) { | |
b024d6a1 EM |
565 | $this->ids['Membership'][0] = $this->contactMembershipCreate([ |
566 | 'contact_id' => $this->getContactID(), | |
567 | $this->getCustomFieldName('text') => 'my field', | |
568 | ]); | |
d41a5d53 EM |
569 | } |
570 | return $this->ids['Membership'][0]; | |
571 | } | |
572 | ||
f399fbd8 EM |
573 | /** |
574 | * Get expected output from token parsing. | |
575 | * | |
576 | * @return string | |
577 | */ | |
e80f2261 | 578 | protected function getExpectedParticipantTokenOutput(): string { |
044c0ad1 EM |
579 | return 'participant.status_id :2 |
580 | participant.role_id :1 | |
581 | participant.register_date :February 19th, 2007 | |
582 | participant.source :Wimbeldon | |
583 | participant.fee_level :steep | |
f70a513f | 584 | participant.fee_amount :$50.00 |
044c0ad1 EM |
585 | participant.registered_by_id : |
586 | participant.transferred_to_contact_id : | |
587 | participant.role_id:label :Attendee | |
588 | participant.balance : | |
589 | participant.custom_2 :99999 | |
590 | participant.id :2 | |
591 | participant.fee_currency :USD | |
592 | participant.discount_amount : | |
593 | participant.status_id:label :Attended | |
594 | participant.status_id:name :Attended | |
595 | participant.role_id:name :Attendee | |
596 | participant.is_test:label :No | |
597 | participant.must_wait : | |
e80f2261 EM |
598 | '; |
599 | } | |
600 | ||
601 | /** | |
602 | * Get expected output from token parsing. | |
603 | * | |
604 | * @return string | |
605 | */ | |
606 | protected function getExpectedEventTokenOutput(): string { | |
044c0ad1 EM |
607 | return 'event.id :' . $this->ids['event'][0] . ' |
608 | event.title :Annual CiviCRM meet | |
609 | event.start_date :October 21st, 2008 | |
610 | event.end_date :October 23rd, 2008 | |
611 | event.event_type_id:label :Conference | |
612 | event.summary :If you have any CiviCRM related issues or want to track where CiviCRM is heading, Sign up now | |
613 | event.contact_email :event@example.com | |
614 | event.contact_phone :456 789 | |
615 | event.description :event description | |
616 | event.location :15 Walton St | |
f399fbd8 EM |
617 | Emerald City, Maine 90210 |
618 | ||
044c0ad1 EM |
619 | event.info_url :' . CRM_Utils_System::url('civicrm/event/info', NULL, TRUE) . '&reset=1&id=1 |
620 | event.registration_url :' . CRM_Utils_System::url('civicrm/event/register', NULL, TRUE) . '&reset=1&id=1 | |
621 | event.custom_1 :my field | |
622 | '; | |
f399fbd8 EM |
623 | } |
624 | ||
d41a5d53 EM |
625 | /** |
626 | * Get expected output from token parsing. | |
627 | * | |
628 | * @return string | |
629 | */ | |
630 | protected function getExpectedMembershipTokenOutput(): string { | |
631 | return ' | |
632 | Expired | |
633 | General | |
634 | 1 | |
635 | Expired | |
636 | General | |
637 | January 21st, 2007 | |
638 | January 21st, 2007 | |
639 | December 21st, 2007 | |
640 | 100.00'; | |
641 | } | |
642 | ||
1ed50dc6 EM |
643 | /** |
644 | * Test that membership tokens are consistently rendered. | |
645 | * | |
646 | * @throws \API_Exception | |
647 | */ | |
648 | public function testParticipantTokenConsistency(): void { | |
649 | $this->createLoggedInUser(); | |
e80f2261 EM |
650 | $this->setupParticipantScheduledReminder(); |
651 | ||
1ed50dc6 | 652 | $tokens = CRM_Core_SelectValues::participantTokens(); |
889b0617 | 653 | $this->assertEquals(array_diff_key($this->getParticipantTokens(), $this->getUnadvertisedTokens()), $tokens); |
e80f2261 EM |
654 | |
655 | $mut = new CiviMailUtils($this); | |
656 | ||
657 | $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [ | |
658 | 'controller' => __CLASS__, | |
659 | 'smarty' => FALSE, | |
660 | 'schema' => ['participantId'], | |
661 | ]); | |
e3a34ebd | 662 | $this->assertEquals(array_merge($tokens, $this->getEventTokens(), $this->getDomainTokens()), $tokenProcessor->listTokens()); |
e80f2261 EM |
663 | |
664 | $this->callAPISuccess('job', 'send_reminder', []); | |
665 | $expected = $this->getExpectedParticipantTokenOutput(); | |
666 | $mut->checkMailLog([$expected]); | |
667 | ||
044c0ad1 | 668 | $tokenProcessor->addMessage('html', $this->getTokenString(array_keys($this->getParticipantTokens())), 'text/plain'); |
e80f2261 EM |
669 | $tokenProcessor->addRow(['participantId' => $this->ids['participant'][0]]); |
670 | $tokenProcessor->evaluate(); | |
671 | $this->assertEquals($expected, $tokenProcessor->getRow(0)->render('html')); | |
672 | ||
1ed50dc6 EM |
673 | } |
674 | ||
2a7cae66 EM |
675 | /** |
676 | * Test that membership tokens are consistently rendered. | |
677 | * | |
678 | * @throws \API_Exception | |
679 | * @throws \CRM_Core_Exception | |
680 | */ | |
681 | public function testParticipantCustomDateToken(): void { | |
682 | $this->createEventAndParticipant(); | |
683 | $dateFieldID = $this->createDateCustomField(['custom_group_id' => $this->ids['CustomGroup']['participant_'], 'default_value' => ''])['id']; | |
684 | $input = '{participant.custom_' . $dateFieldID . '}'; | |
685 | $input .= '{participant.' . $this->getCustomFieldName('participant_int') . '}'; | |
686 | $tokenHtml = CRM_Core_BAO_MessageTemplate::renderTemplate([ | |
687 | 'messageTemplate' => ['msg_html' => $input], | |
688 | 'tokenContext' => array_merge(['participantId' => $this->ids['participant'][0]], ['schema' => ['participantId', 'eventId']]), | |
689 | ])['html']; | |
690 | $this->assertEquals(99999, $tokenHtml); | |
691 | } | |
692 | ||
1ed50dc6 | 693 | /** |
3c78698e | 694 | * Get declared participant tokens. |
1ed50dc6 EM |
695 | * |
696 | * @return string[] | |
697 | */ | |
698 | public function getParticipantTokens(): array { | |
699 | return [ | |
b7472bd6 | 700 | '{participant.status_id}' => 'Status ID', |
e80f2261 | 701 | '{participant.role_id}' => 'Participant Role ID', |
b7472bd6 EM |
702 | '{participant.register_date}' => 'Register date', |
703 | '{participant.source}' => 'Participant Source', | |
704 | '{participant.fee_level}' => 'Fee level', | |
705 | '{participant.fee_amount}' => 'Fee Amount', | |
706 | '{participant.registered_by_id}' => 'Registered By Participant ID', | |
1ed50dc6 | 707 | '{participant.transferred_to_contact_id}' => 'Transferred to Contact ID', |
e80f2261 EM |
708 | '{participant.role_id:label}' => 'Participant Role', |
709 | '{participant.balance}' => 'Event Balance', | |
710 | '{participant.' . $this->getCustomFieldName('participant_int') . '}' => 'Enter integer here :: participant_Group with field int', | |
711 | '{participant.id}' => 'Participant ID', | |
712 | '{participant.fee_currency}' => 'Fee Currency', | |
713 | '{participant.discount_amount}' => 'Discount Amount', | |
714 | '{participant.status_id:label}' => 'Status', | |
715 | '{participant.status_id:name}' => 'Machine name: Status', | |
716 | '{participant.role_id:name}' => 'Machine name: Participant Role', | |
717 | '{participant.is_test:label}' => 'Test', | |
718 | '{participant.must_wait}' => 'Must Wait on List', | |
1ed50dc6 EM |
719 | ]; |
720 | } | |
721 | ||
3c78698e EM |
722 | /** |
723 | * Test that domain tokens are consistently rendered. | |
724 | */ | |
725 | public function testDomainTokenConsistency(): void { | |
726 | $tokens = CRM_Core_SelectValues::domainTokens(); | |
727 | $this->assertEquals($this->getDomainTokens(), $tokens); | |
728 | $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [ | |
729 | 'controller' => __CLASS__, | |
730 | 'smarty' => FALSE, | |
731 | ]); | |
732 | $tokens['{domain.id}'] = 'Domain ID'; | |
733 | $tokens['{domain.description}'] = 'Domain Description'; | |
dfe53edd | 734 | $tokens['{domain.now}'] = 'Current time/date'; |
3c78698e EM |
735 | $this->assertEquals($tokens, $tokenProcessor->listTokens()); |
736 | } | |
737 | ||
defba8ff EM |
738 | /** |
739 | * @throws \API_Exception | |
740 | * @throws \CRM_Core_Exception | |
741 | */ | |
742 | public function testDomainNow(): void { | |
743 | putenv('TIME_FUNC=frozen'); | |
5e62af3d | 744 | CRM_Utils_Time::setTime('2021-09-18 23:58:00'); |
44dd64f0 EM |
745 | $modifiers = [ |
746 | 'shortdate' => '09/18/2021', | |
f85e1a4b | 747 | '%B %Y' => 'September 2021', |
44dd64f0 EM |
748 | ]; |
749 | foreach ($modifiers as $filter => $expected) { | |
750 | $resolved = CRM_Core_BAO_MessageTemplate::renderTemplate([ | |
751 | 'messageTemplate' => [ | |
f85e1a4b | 752 | 'msg_text' => '{domain.now|crmDate:"' . $filter . '"}', |
44dd64f0 EM |
753 | ], |
754 | ])['text']; | |
f85e1a4b | 755 | $this->assertEquals($expected, $resolved); |
44dd64f0 | 756 | } |
defba8ff EM |
757 | $resolved = CRM_Core_BAO_MessageTemplate::renderTemplate([ |
758 | 'messageTemplate' => [ | |
759 | 'msg_text' => '{domain.now}', | |
760 | ], | |
761 | ])['text']; | |
762 | $this->assertEquals('September 18th, 2021 11:58 PM', $resolved); | |
f85e1a4b TO |
763 | |
764 | // This example is malformed - no quotes | |
765 | try { | |
766 | $resolved = CRM_Core_BAO_MessageTemplate::renderTemplate([ | |
767 | 'messageTemplate' => [ | |
768 | 'msg_text' => '{domain.now|crmDate:shortdate}', | |
769 | ], | |
770 | ])['text']; | |
e80f2261 | 771 | $this->fail('Expected unquoted parameter to fail'); |
f85e1a4b TO |
772 | } |
773 | catch (\CRM_Core_Exception $e) { | |
774 | $this->assertRegExp(';Malformed token param;', $e->getMessage()); | |
775 | } | |
defba8ff EM |
776 | } |
777 | ||
3c78698e EM |
778 | /** |
779 | * Get declared participant tokens. | |
780 | * | |
781 | * @return string[] | |
782 | */ | |
783 | public function getDomainTokens(): array { | |
784 | return [ | |
785 | '{domain.name}' => ts('Domain name'), | |
786 | '{domain.address}' => ts('Domain (organization) address'), | |
787 | '{domain.phone}' => ts('Domain (organization) phone'), | |
788 | '{domain.email}' => 'Domain (organization) email', | |
789 | '{domain.id}' => ts('Domain ID'), | |
790 | '{domain.description}' => ts('Domain Description'), | |
defba8ff | 791 | '{domain.now}' => 'Current time/date', |
b8fe55cd | 792 | '{domain.tax_term}' => 'Sales tax term (e.g VAT)', |
3c78698e EM |
793 | ]; |
794 | } | |
795 | ||
ce971869 | 796 | /** |
e80f2261 | 797 | * Test that event tokens are consistently rendered. |
34795e7a EM |
798 | * |
799 | * @throws \API_Exception | |
ce971869 EM |
800 | */ |
801 | public function testEventTokenConsistency(): void { | |
f399fbd8 EM |
802 | $mut = new CiviMailUtils($this); |
803 | $this->setupParticipantScheduledReminder(); | |
804 | ||
ce971869 | 805 | $tokens = CRM_Core_SelectValues::eventTokens(); |
889b0617 | 806 | $this->assertEquals(array_merge($this->getEventTokens()), $tokens); |
ce971869 EM |
807 | $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [ |
808 | 'controller' => __CLASS__, | |
809 | 'smarty' => FALSE, | |
810 | 'schema' => ['eventId'], | |
811 | ]); | |
812 | $this->assertEquals(array_merge($tokens, $this->getDomainTokens()), $tokenProcessor->listTokens()); | |
f399fbd8 | 813 | |
044c0ad1 | 814 | $expectedEventString = $this->getExpectedEventTokenOutput(); |
e80f2261 | 815 | $this->callAPISuccess('job', 'send_reminder', []); |
044c0ad1 EM |
816 | $expectedParticipantString = $this->getExpectedParticipantTokenOutput(); |
817 | $toCheck = array_merge(explode("\n", $expectedEventString), explode("\n", $expectedParticipantString)); | |
818 | $toCheck[] = $expectedEventString; | |
819 | $toCheck[] = $expectedParticipantString; | |
820 | $mut->checkMailLog($toCheck); | |
821 | $tokens = array_keys($this->getEventTokens()); | |
822 | $html = $this->getTokenString($tokens); | |
823 | $tokenProcessor->addMessage('html', $html, 'text/plain'); | |
e80f2261 EM |
824 | $tokenProcessor->addRow(['eventId' => $this->ids['event'][0]]); |
825 | $tokenProcessor->evaluate(); | |
044c0ad1 | 826 | $this->assertEquals($expectedEventString, $tokenProcessor->getRow(0)->render('html')); |
e80f2261 EM |
827 | } |
828 | ||
829 | /** | |
830 | * Test that event tokens work absent participant tokens. | |
831 | * | |
832 | * @throws \API_Exception | |
833 | */ | |
834 | public function testEventTokenConsistencyNoParticipantTokens(): void { | |
835 | $mut = new CiviMailUtils($this); | |
836 | $this->setupParticipantScheduledReminder(FALSE); | |
837 | ||
f399fbd8 EM |
838 | $this->callAPISuccess('job', 'send_reminder', []); |
839 | $expected = $this->getExpectedEventTokenOutput(); | |
044c0ad1 EM |
840 | // Checking these individually is easier to decipher discrepancies |
841 | // but we also want to check in entirety. | |
842 | $toCheck = explode("\n", $expected); | |
843 | $toCheck[] = $expected; | |
844 | $mut->checkMailLog($toCheck); | |
e80f2261 EM |
845 | |
846 | $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [ | |
847 | 'controller' => __CLASS__, | |
848 | 'smarty' => FALSE, | |
849 | 'schema' => ['eventId'], | |
850 | ]); | |
044c0ad1 | 851 | $html = $this->getTokenString(array_keys($this->getEventTokens())); |
e80f2261 | 852 | |
044c0ad1 | 853 | $tokenProcessor->addMessage('html', $html, 'text/plain'); |
e80f2261 EM |
854 | $tokenProcessor->addRow(['eventId' => $this->ids['event'][0]]); |
855 | $tokenProcessor->evaluate(); | |
856 | $this->assertEquals($expected, $tokenProcessor->getRow(0)->render('html')); | |
857 | ||
f399fbd8 EM |
858 | } |
859 | ||
860 | /** | |
861 | * Set up scheduled reminder for participants. | |
862 | * | |
863 | * @throws \API_Exception | |
864 | */ | |
e80f2261 | 865 | public function setupParticipantScheduledReminder($includeParticipant = TRUE): void { |
2a7cae66 | 866 | $this->createEventAndParticipant(); |
044c0ad1 | 867 | $tokens = array_keys($this->getEventTokens()); |
e80f2261 | 868 | if ($includeParticipant) { |
044c0ad1 | 869 | $tokens = array_keys(array_merge($this->getEventTokens(), $this->getParticipantTokens())); |
e80f2261 | 870 | } |
044c0ad1 | 871 | $html = $this->getTokenString($tokens); |
f399fbd8 EM |
872 | CRM_Utils_Time::setTime('2007-02-20 15:00:00'); |
873 | $this->callAPISuccess('action_schedule', 'create', [ | |
874 | 'title' => 'job', | |
875 | 'subject' => 'job', | |
876 | 'entity_value' => 1, | |
877 | 'mapping_id' => 2, | |
878 | 'start_action_date' => 'register_date', | |
879 | 'start_action_offset' => 1, | |
880 | 'start_action_condition' => 'after', | |
881 | 'start_action_unit' => 'day', | |
e80f2261 | 882 | 'body_html' => $html, |
f399fbd8 | 883 | ]); |
ce971869 EM |
884 | } |
885 | ||
886 | /** | |
887 | * Get expected event tokens. | |
888 | * | |
889 | * @return string[] | |
890 | */ | |
891 | protected function getEventTokens(): array { | |
892 | return [ | |
e80f2261 | 893 | '{event.id}' => 'Event ID', |
ce971869 EM |
894 | '{event.title}' => 'Event Title', |
895 | '{event.start_date}' => 'Event Start Date', | |
896 | '{event.end_date}' => 'Event End Date', | |
e80f2261 | 897 | '{event.event_type_id:label}' => 'Event Type', |
ce971869 EM |
898 | '{event.summary}' => 'Event Summary', |
899 | '{event.contact_email}' => 'Event Contact Email', | |
900 | '{event.contact_phone}' => 'Event Contact Phone', | |
901 | '{event.description}' => 'Event Description', | |
902 | '{event.location}' => 'Event Location', | |
ce971869 EM |
903 | '{event.info_url}' => 'Event Info URL', |
904 | '{event.registration_url}' => 'Event Registration URL', | |
f399fbd8 | 905 | '{event.' . $this->getCustomFieldName('text') . '}' => 'Enter text here :: Group with field text', |
ce971869 EM |
906 | ]; |
907 | } | |
908 | ||
044c0ad1 EM |
909 | /** |
910 | * @param array $tokens | |
911 | * | |
912 | * @return string | |
913 | */ | |
914 | protected function getTokenString(array $tokens): string { | |
915 | $html = ''; | |
916 | foreach ($tokens as $token) { | |
917 | $html .= substr($token, 1, -1) . ' :' . $token . "\n"; | |
918 | } | |
919 | return $html; | |
920 | } | |
921 | ||
2a7cae66 EM |
922 | /** |
923 | * Create an event with a participant. | |
924 | * | |
925 | * @throws \API_Exception | |
926 | */ | |
927 | protected function createEventAndParticipant(): void { | |
928 | $this->createCustomGroupWithFieldOfType(['extends' => 'Event']); | |
929 | $this->createCustomGroupWithFieldOfType(['extends' => 'Participant'], 'int', 'participant_'); | |
930 | $emailID = Email::create() | |
931 | ->setValues(['email' => 'event@example.com']) | |
932 | ->execute() | |
933 | ->first()['id']; | |
934 | $addressID = Address::create()->setValues([ | |
935 | 'street_address' => '15 Walton St', | |
936 | 'supplemental_address_1' => 'up the road', | |
937 | 'city' => 'Emerald City', | |
938 | 'state_province_id:label' => 'Maine', | |
939 | 'postal_code' => 90210, | |
940 | ])->execute()->first()['id']; | |
941 | $phoneID = Phone::create() | |
942 | ->setValues(['phone' => '456 789']) | |
943 | ->execute() | |
944 | ->first()['id']; | |
945 | ||
946 | $locationBlockID = LocBlock::save(FALSE)->setRecords([ | |
947 | [ | |
948 | 'email_id' => $emailID, | |
949 | 'address_id' => $addressID, | |
950 | 'phone_id' => $phoneID, | |
951 | ], | |
952 | ])->execute()->first()['id']; | |
953 | $this->ids['event'][0] = $this->eventCreate([ | |
954 | 'description' => 'event description', | |
955 | $this->getCustomFieldName('text') => 'my field', | |
956 | 'loc_block_id' => $locationBlockID, | |
957 | ])['id']; | |
958 | // Create an unrelated participant record so that the ids don't match. | |
959 | // this prevents things working just because the id 'happens to be valid' | |
960 | $this->participantCreate([ | |
961 | 'register_date' => '2020-01-01', | |
962 | 'event_id' => $this->ids['event'][0], | |
963 | ]); | |
964 | $this->ids['participant'][0] = $this->participantCreate([ | |
965 | 'event_id' => $this->ids['event'][0], | |
966 | 'fee_amount' => 50, | |
967 | 'fee_level' => 'steep', | |
968 | $this->getCustomFieldName('participant_int') => '99999', | |
969 | ]); | |
970 | } | |
971 | ||
b05a7c72 | 972 | public function testEscaping() { |
167f848f TO |
973 | $autoClean = []; |
974 | $create = function(string $entity, array $record = []) use (&$autoClean) { | |
975 | // It's convenient to use createTestObject(), but it doesn't reproduce the normal escaping rules from QuickForm/APIv3/APIv4. | |
b05a7c72 | 976 | CRM_Utils_API_HTMLInputCoder::singleton()->encodeRow($record); |
167f848f TO |
977 | $dao = CRM_Core_DAO::createTestObject(CRM_Core_DAO_AllCoreTables::getFullName($entity), $record); |
978 | ||
979 | // We're not using transactions, and truncating 'contact' seems problematic, so we roll up our sleeves and cleanup each record... | |
980 | $autoClean[] = CRM_Utils_AutoClean::with(function() use ($entity, $dao) { | |
981 | CRM_Core_DAO::deleteTestObjects(CRM_Core_DAO_AllCoreTables::getFullName($entity), ['id' => $dao->id]); | |
982 | }); | |
983 | ||
984 | return $dao; | |
b05a7c72 TO |
985 | }; |
986 | ||
987 | $context = []; | |
988 | $context['contactId'] = $create('Contact', [ | |
989 | 'first_name' => '<b>ig</b>illy brackets', | |
990 | ])->id; | |
991 | $context['eventId'] = $create('Event', [ | |
992 | 'title' => 'The Webinar', | |
993 | 'description' => '<p>Some online webinar thingy.</p> <p>Attendees will need to install the <a href="http://telefoo.example.com">TeleFoo</a> app.</p>', | |
994 | ])->id; | |
995 | ||
996 | $messages = $expected = []; | |
997 | ||
998 | // The `first_name` does not allow HTML. Any funny characters are presented like literal text. | |
999 | $messages['contact_text'] = 'Hello {contact.first_name}!'; | |
b9466f09 | 1000 | $expected['contact_text'] = 'Hello <b>ig</b>illy brackets!'; |
b05a7c72 | 1001 | |
b9466f09 EM |
1002 | $messages['contact_html'] = '<p>Hello {contact.first_name}!</p>'; |
1003 | $expected['contact_html'] = '<p>Hello <b>ig</b>illy brackets!</p>'; | |
b05a7c72 TO |
1004 | |
1005 | // The `description` does allow HTML. Any funny characters are filtered out of text. | |
1006 | $messages['event_text'] = 'You signed up for this event: {event.title}: {event.description}'; | |
1007 | $expected['event_text'] = 'You signed up for this event: The Webinar: Some online webinar thingy. Attendees will need to install the TeleFoo app.'; | |
1008 | ||
b9466f09 | 1009 | $messages['event_html'] = '<p>You signed up for this event:</p> <h3>{event.title}</h3> {event.description}'; |
b05a7c72 TO |
1010 | $expected['event_html'] = '<p>You signed up for this event:</p> <h3>The Webinar</h3> <p>Some online webinar thingy.</p> <p>Attendees will need to install the <a href="http://telefoo.example.com">TeleFoo</a> app.</p>'; |
1011 | ||
1012 | $rendered = CRM_Core_TokenSmarty::render($messages, $context); | |
1013 | ||
1014 | $this->assertEquals($expected, $rendered); | |
1015 | } | |
1016 | ||
d0ce76fd | 1017 | } |