(REF) CRM_Utils_Array - Add pathSync() helper
[civicrm-core.git] / tests / events / hook_civicrm_alterMailParams.evch.php
CommitLineData
1348d066
TO
1<?php
2return new class() extends \Civi\Test\EventCheck implements \Civi\Test\HookInterface {
3
4 private $paramSpecs = [
5
6 // ## Envelope: Common
7
8 'toName' => ['type' => 'string|NULL'],
9 'toEmail' => ['type' => 'string|NULL'],
10 'cc' => ['type' => 'string|NULL'],
11 'bcc' => ['type' => 'string|NULL'],
12 'headers' => ['type' => 'array'],
13 'attachments' => ['type' => 'array|NULL'],
14 'isTest' => ['type' => 'bool|int'],
15
16 // ## Envelope: singleEmail/messageTemplate
17
18 'from' => ['type' => 'string|NULL', 'for' => ['messageTemplate', 'singleEmail']],
19 'replyTo' => ['type' => 'string|NULL', 'for' => ['messageTemplate', 'singleEmail']],
20 'returnPath' => ['type' => 'string|NULL', 'for' => ['messageTemplate', 'singleEmail']],
21 'isEmailPdf' => ['type' => 'bool', 'for' => 'messageTemplate'],
22 'PDFFilename' => ['type' => 'string|NULL', 'for' => 'messageTemplate'],
23 'autoSubmitted' => ['type' => 'bool', 'for' => 'messageTemplate'],
24 'Message-ID' => ['type' => 'string', 'for' => ['messageTemplate', 'singleEmail']],
25 'messageId' => ['type' => 'string', 'for' => ['messageTemplate', 'singleEmail']],
26
27 // ## Envelope: CiviMail/Flexmailer
28
29 'Reply-To' => ['type' => 'string|NULL', 'for' => ['civimail', 'flexmailer']],
30 'Return-Path' => ['type' => 'string|NULL', 'for' => ['civimail', 'flexmailer']],
31 'From' => ['type' => 'string|NULL', 'for' => ['civimail', 'flexmailer']],
32 'Subject' => ['type' => 'string|NULL', 'for' => ['civimail', 'flexmailer']],
33 'List-Unsubscribe' => ['type' => 'string|NULL', 'for' => ['civimail', 'flexmailer']],
34 'X-CiviMail-Bounce' => ['type' => 'string|NULL', 'for' => ['civimail', 'flexmailer']],
35 'Precedence' => ['type' => 'string|NULL', 'for' => ['civimail', 'flexmailer'], 'regex' => '/(bulk|first-class|list)/'],
36 'job_id' => ['type' => 'int|NULL', 'for' => ['civimail', 'flexmailer']],
37
38 // ## Content
39
40 'subject' => ['for' => ['messageTemplate', 'singleEmail'], 'type' => 'string'],
41 'text' => ['type' => 'string|NULL'],
42 'html' => ['type' => 'string|NULL'],
43
44 // ## Model: messageTemplate
45
46 'tokenContext' => ['type' => 'array', 'for' => 'messageTemplate'],
47 'tplParams' => ['type' => 'array', 'for' => 'messageTemplate'],
48 'contactId' => ['type' => 'int|NULL', 'for' => 'messageTemplate' /* deprecated in favor of tokenContext[contactId] */],
49 'valueName' => [
50 'regex' => '/^([a-zA-Z_]+)$/',
51 'type' => 'string',
52 'for' => 'messageTemplate',
53 ],
54 'groupName' => [
55 // This field is generally deprecated. Historically, this was tied to various option-groups (`msg_*`),
56 // but it also seems to have been used with a few long-form English names.
57 'regex' => '/^(msg_[a-zA-Z_]+|Scheduled Reminder Sender|Activity Email Sender|Report Email Sender|Mailing Event Welcome|CRM_Core_Config_MailerTest)$/',
58 'type' => 'string',
59 'for' => ['messageTemplate', 'singleEmail'],
60 ],
61
62 // The model is not passed into this hook because it would create ambiguity when you alter properties.
63 // If you want to expose it via hook, add another hook.
64 'model' => ['for' => 'messageTemplate', 'type' => 'NULL'],
65 'modelProps' => ['for' => 'messageTemplate', 'type' => 'NULL'],
66
67 // ## Model: Adhoc/incomplete/needs attention
68
69 'contributionId' => ['type' => 'int', 'for' => 'messageTemplate'],
70 'petitionId' => ['type' => 'int', 'for' => 'messageTemplate'],
71 'petitionTitle' => ['type' => 'string', 'for' => 'messageTemplate'],
72 'table' => ['type' => 'string', 'for' => 'messageTemplate', 'regex' => '/civicrm_msg_template/'],
73 'entity' => ['type' => 'string|NULL', 'for' => 'singleEmail'],
74 'entity_id' => ['type' => 'int|NULL', 'for' => 'singleEmail'],
75
76 // ## View: messageTemplate
77
78 'messageTemplateID' => ['type' => 'int|NULL', 'for' => 'messageTemplate'],
79 'messageTemplate' => ['type' => 'array|NULL', 'for' => 'messageTemplate'],
80 'disableSmarty' => ['type' => 'bool|int', 'for' => 'messageTemplate'],
81 ];
82
83 public function isSupported($test) {
84 // MailTest does intentionally breaky things to provoke+ensure decent error-handling.
85 //So we will not enforce generic rules on it.
86 return !($test instanceof CRM_Utils_MailTest);
87 }
88
89 /**
90 * Ensure that the hook data is always well-formed.
91 *
92 * @see \CRM_Utils_Hook::alterMailParams()
93 */
94 public function hook_civicrm_alterMailParams(&$params, $context = NULL) {
95 $msg = "Non-conformant hook_civicrm_alterMailParams(..., $context)";
96 $dump = print_r($params, 1);
97
98 $this->assertRegExp('/^(messageTemplate|civimail|singleEmail|flexmailer)$/',
99 $context, "$msg: Unrecognized context ($context)\n$dump");
100
101 $contexts = [$context];
102 if ($context === 'singleEmail' && array_key_exists('tokenContext', $params)) {
103 // Don't look now, but `sendTemplate()` fires this hook twice for the message! Once with $context=messageTemplate; again with $context=singleEmail.
104 $contexts[] = 'messageTemplate';
105 }
106
107 $paramSpecs = array_filter($this->paramSpecs, function ($f) use ($contexts) {
108 return !isset($f['for']) || array_intersect((array) $f['for'], $contexts);
109 });
110
111 $unknownKeys = array_diff(array_keys($params), array_keys($paramSpecs));
112 if ($unknownKeys !== []) {
113 echo '';
114 }
115 $this->assertEquals([], $unknownKeys, "$msg: Unrecognized keys: " . implode(', ', $unknownKeys) . "\n$dump");
116
117 foreach ($params as $key => $value) {
118 $this->assertType($paramSpecs[$key]['type'], $value, "$msg: Bad data-type found in param ($key)\n$dump");
119 if (isset($paramSpecs[$key]['regex'])) {
120 $this->assertRegExp($paramSpecs[$key]['regex'], $value, "Parameter [$key => $value] should match regex ({$paramSpecs[$key]['regex']})");
121 }
122 }
123
124 if ($context === 'messageTemplate') {
125 $this->assertTrue(!empty($params['valueName']), "$msg: Message templates must always specify the name of the workflow step\n$dump");
126 $this->assertEquals($params['contactId'] ?? NULL, $params['tokenContext']['contactId'] ?? NULL, "$msg: contactId moved to tokenContext, but legacy value should be equivalent\n$dump");
127
128 // This assertion is surprising -- yet true. We should perhaps check if it was true in past releases...
129 $this->assertTrue(empty($params['text']) && empty($params['html']) && empty($params['subject']), "$msg: Content is not given if context==messageTemplate\n$dump");
130 }
131
132 if ($context !== 'messageTemplate') {
133 $this->assertTrue(!empty($params['text']) || !empty($params['html']) || !empty($params['subject']), "$msg: Must provide at least one of: text, html, subject\n$dump");
134 }
135
136 if (isset($params['groupName']) && $params['groupName'] === 'Scheduled Reminder Sender') {
137 $this->assertTrue(!empty($params['entity']), "$msg: Scheduled reminders should have entity\n$dump");
138 $this->assertTrue(!empty($params['entity_id']), "$msg: Scheduled reminders should have entity_id\n$dump");
139 }
140 }
141
142};