Merge pull request #16419 from civicrm/5.22
[civicrm-core.git] / tests / phpunit / CRM / Activity / Form / ActivityTest.php
1 <?php
2
3 /**
4 * Include dataProvider for tests
5 * @group headless
6 */
7 class CRM_Activity_Form_ActivityTest extends CiviUnitTestCase {
8
9 public function setUp() {
10 parent::setUp();
11 $this->assignee1 = $this->individualCreate([
12 'first_name' => 'testassignee1',
13 'last_name' => 'testassignee1',
14 'email' => 'testassignee1@gmail.com',
15 ]);
16 $this->assignee2 = $this->individualCreate([
17 'first_name' => 'testassignee2',
18 'last_name' => 'testassignee2',
19 'email' => 'testassignee2@gmail.com',
20 ]);
21 $this->target = $this->individualCreate();
22 $this->source = $this->individualCreate();
23 }
24
25 public function testActivityCreate() {
26 Civi::settings()->set('activity_assignee_notification', TRUE);
27 //Reset filter to none.
28 Civi::settings()->set('do_not_notify_assignees_for', []);
29 $mut = new CiviMailUtils($this, TRUE);
30 $mut->clearMessages();
31
32 $form = new CRM_Activity_Form_Activity();
33 $activityTypeId = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'activity_type_id', 'Meeting');
34 $params = [
35 'source_contact_id' => $this->source,
36 'assignee_contact_id' => [$this->assignee1],
37 'target_contact_id' => [$this->target],
38 'followup_assignee_contact_id' => [],
39 'activity_type_id' => $activityTypeId,
40 ];
41
42 $activityRef = new ReflectionClass('CRM_Activity_Form_Activity');
43 $method = $activityRef->getMethod('processActivity');
44 $method->setAccessible(TRUE);
45 $method->invokeArgs($form, [&$params]);
46
47 $msg = $mut->getMostRecentEmail();
48 $this->assertNotEmpty($msg);
49 $mut->clearMessages();
50
51 //Block Meeting notification.
52 Civi::settings()->set('do_not_notify_assignees_for', [$activityTypeId]);
53 $params['assignee_contact_id'] = [$this->assignee2];
54 $method->invokeArgs($form, [&$params]);
55 $msg = $mut->getMostRecentEmail();
56 $this->assertEmpty($msg);
57 }
58
59 public function testActivityDelete() {
60 // Set the parameters of the test.
61 $numberOfSingleActivitiesToCreate = 3;
62 $numberOfRepeatingActivitiesToCreate = 6;
63 $singleActivityToDeleteOffset = 1;
64 $mode1ActivityToDeleteOffset = 1;
65 $mode2ActivityToDeleteOffset = 3;
66 $mode3ActivityToDeleteOffset = 2;
67
68 // Track the target contact's activities.
69 $expectedActivityIds = array_keys(CRM_Activity_BAO_Activity::getActivities(['contact_id' => $this->target]));
70
71 // Create non-repeating activities.
72 $meetingActivityTypeId = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'activity_type_id', 'Meeting');
73 $singleActivityIds = [];
74 for ($activityCount = 0; $activityCount < $numberOfSingleActivitiesToCreate; $activityCount++) {
75 $activityParams = [
76 'source_contact_id' => $this->source,
77 'target_contact_id' => $this->target,
78 'activity_type_id' => $meetingActivityTypeId,
79 'activity_date_time' => date_create('+' . $activityCount . ' weeks')->format('YmdHis'),
80 ];
81 $singleActivityBao = CRM_Activity_BAO_Activity::create($activityParams);
82 $singleActivityIds[] = $singleActivityBao->id;
83 }
84 $expectedActivityIds = array_merge($expectedActivityIds, $singleActivityIds);
85
86 // Create an activity to be repeated.
87 $activityParams = [
88 'source_contact_id' => $this->source,
89 'target_contact_id' => $this->target,
90 'activity_type_id' => $meetingActivityTypeId,
91 'activity_date_time' => date('YmdHis'),
92 ];
93 $repeatingActivityBao = CRM_Activity_BAO_Activity::create($activityParams);
94
95 // Create the repeating activity's schedule.
96 $actionScheduleParams = [
97 'used_for' => 'civicrm_activity',
98 'entity_value' => $repeatingActivityBao->id,
99 'start_action_date' => $repeatingActivityBao->activity_date_time,
100 'repetition_frequency_unit' => 'week',
101 'repetition_frequency_interval' => 1,
102 'start_action_offset' => $numberOfRepeatingActivitiesToCreate - 1,
103 ];
104 $actionScheduleBao = CRM_Core_BAO_ActionSchedule::add($actionScheduleParams);
105
106 // Create the activity's repeats.
107 $recurringEntityBao = new CRM_Core_BAO_RecurringEntity();
108 $recurringEntityBao->entity_table = 'civicrm_activity';
109 $recurringEntityBao->entity_id = $repeatingActivityBao->id;
110 $recurringEntityBao->dateColumns = ['activity_date_time'];
111 $recurringEntityBao->linkedEntities = [
112 [
113 'table' => 'civicrm_activity_contact',
114 'findCriteria' => ['activity_id' => $repeatingActivityBao->id],
115 'linkedColumns' => ['activity_id'],
116 'isRecurringEntityRecord' => FALSE,
117 ],
118 ];
119 $recurringEntityBao->scheduleId = $actionScheduleBao->id;
120 $newEntities = $recurringEntityBao->generate();
121 $repeatingActivityIds = array_merge([$repeatingActivityBao->id], $newEntities['civicrm_activity']);
122 $expectedActivityIds = array_merge($expectedActivityIds, $repeatingActivityIds);
123
124 // Assert that the expected activities exist.
125 $this->assertTargetActivityIds($expectedActivityIds);
126
127 // Delete a non-repeating activity.
128 $activityId = $singleActivityIds[$singleActivityToDeleteOffset];
129 $this->deleteActivity($activityId);
130 $expectedActivityIds = array_diff($expectedActivityIds, [$activityId]);
131 $this->assertTargetActivityIds($expectedActivityIds);
132
133 // Delete one activity from series (mode 1).
134 $activityId = $repeatingActivityIds[$mode1ActivityToDeleteOffset];
135 $this->deleteActivity($activityId, 1);
136 $expectedActivityIds = array_diff($expectedActivityIds, [$activityId]);
137 $this->assertTargetActivityIds($expectedActivityIds);
138
139 // Delete from one activity until end of series (mode 2).
140 $activityId = $repeatingActivityIds[$mode2ActivityToDeleteOffset];
141 $this->deleteActivity($activityId, 2);
142 $expectedActivityIds = array_diff($expectedActivityIds, array_slice($repeatingActivityIds, $mode2ActivityToDeleteOffset));
143 $this->assertTargetActivityIds($expectedActivityIds);
144
145 // Delete all activities in series (mode 3).
146 $activityId = $repeatingActivityIds[$mode3ActivityToDeleteOffset];
147 $this->deleteActivity($activityId, 3);
148 $expectedActivityIds = array_diff($expectedActivityIds, $repeatingActivityIds);
149 $this->assertTargetActivityIds($expectedActivityIds);
150 }
151
152 /**
153 * Asserts that the target contact has the expected activity IDs
154 *
155 * @param array $expectedActivityIds
156 * An array of the activity IDs that are expected to exist for the target contact
157 */
158 private function assertTargetActivityIds($expectedActivityIds) {
159 $actualActivityIds = array_keys(CRM_Activity_BAO_Activity::getActivities(['contact_id' => $this->target]));
160 $this->assertEquals(array_fill_keys($expectedActivityIds, NULL), array_fill_keys($actualActivityIds, NULL));
161 }
162
163 /**
164 * Tests the form's deletion of activities, with optional mode for repeating activities
165 *
166 * @param int $activityId
167 * The ID of the activity to delete
168 * @param int $mode
169 * 1 - delete the specified activity
170 * 2 - delete the specified activity and all following activities in the series
171 * 3 - delete all activities in the series
172 */
173 private function deleteActivity($activityId, $mode = NULL) {
174 // For repeating activities, set the recurring entity mode.
175 if (!is_null($mode)) {
176 $recurringEntityBao = new CRM_Core_BAO_RecurringEntity();
177 $recurringEntityBao->entity_table = 'civicrm_activity';
178 $recurringEntityBao->entity_id = $activityId;
179 $recurringEntityBao->mode($mode);
180 }
181
182 // Use a form to delete the activity.
183 $form = new CRM_Activity_Form_Activity();
184 $form->_action = CRM_Core_Action::DELETE;
185 $form->_activityId = $activityId;
186 $form->postProcess();
187 }
188
189 /**
190 * This is a bit messed up having a variable called name that means label but we don't want to fix it because it's a form member variable _activityTypeName that might be used in form hooks, so just make sure it doesn't flip between name and label. dev/core#1116
191 */
192 public function testActivityTypeNameIsReallyLabel() {
193 $form = new CRM_Activity_Form_Activity();
194
195 // the actual value is irrelevant we just need something for the tested function to act on
196 $form->_currentlyViewedContactId = $this->source;
197
198 // Let's make a new activity type that has a different name from its label just to be sure.
199 $actParams = [
200 'option_group_id' => 'activity_type',
201 'name' => 'wp1234',
202 'label' => 'Water Plants',
203 'is_active' => 1,
204 'is_default' => 0,
205 ];
206 $result = $this->callAPISuccess('option_value', 'create', $actParams);
207
208 $form->_activityTypeId = $result['values'][$result['id']]['value'];
209 $this->assertNotEmpty($form->_activityTypeId);
210
211 // Do the thing we want to test
212 $form->assignActivityType();
213
214 $this->assertEquals('Water Plants', $form->_activityTypeName);
215
216 // cleanup
217 $this->callAPISuccess('option_value', 'delete', ['id' => $result['id']]);
218 }
219
220 /**
221 * Test that the machineName and displayLabel are assigned correctly to the
222 * smarty template.
223 *
224 * See also testActivityTypeNameIsReallyLabel()
225 */
226 public function testActivityTypeAssignment() {
227 $form = new CRM_Activity_Form_Activity();
228
229 $form->_currentlyViewedContactId = $this->source;
230
231 // Let's make a new activity type that has a different name from its label just to be sure.
232 $actParams = [
233 'option_group_id' => 'activity_type',
234 'name' => '47395hc',
235 'label' => 'Hide Cookies',
236 'is_active' => 1,
237 'is_default' => 0,
238 ];
239 $result = $this->callAPISuccess('option_value', 'create', $actParams);
240
241 $form->_activityTypeId = $result['values'][$result['id']]['value'];
242
243 // Do the thing we want to test
244 $form->assignActivityType();
245
246 // Check the smarty template has the correct values assigned.
247 $keyValuePair = $form->getTemplate()->get_template_vars('activityTypeNameAndLabel');
248 $this->assertEquals('47395hc', $keyValuePair['machineName']);
249 $this->assertEquals('Hide Cookies', $keyValuePair['displayLabel']);
250
251 // cleanup
252 $this->callAPISuccess('option_value', 'delete', ['id' => $result['id']]);
253 }
254
255 /**
256 * Test that inbound email is still treated properly if you change the label.
257 * I'm not crazy about the strategy used in this test but I can't see another
258 * way to do it.
259 */
260 public function testInboundEmailDisplaysWithLinebreaks() {
261 // Change label
262 $inbound_email = $this->callAPISuccess('OptionValue', 'getsingle', [
263 'option_group_id' => 'activity_type',
264 'name' => 'Inbound Email',
265 ]);
266 $this->callAPISuccess('OptionValue', 'create', [
267 'id' => $inbound_email['id'],
268 'label' => 'Probably Spam',
269 ]);
270
271 // Fake an inbound email and store it
272
273 $messageBody = <<<ENDBODY
274 -ALTERNATIVE ITEM 0-
275 Hi,
276
277 Wassup!?!?
278
279 Let's check if the output when viewing the form has legible line breaks in the output.
280
281 Thanks!
282
283 -ALTERNATIVE ITEM 1-
284
285 <div dir="ltr">Hi,<br></div>
286 <div dir="ltr"><br></div>
287 <div dir="ltr">Wassup!?!?<br></div>
288 <div dir="ltr"><br></div>
289 <div dir="ltr">Let&#39;s check if the output when viewing the form has legible line breaks in the output.<br></div>
290 <div dir="ltr"><br></div>
291 <div dir="ltr">Thanks!<br></div>
292 -ALTERNATIVE END-
293 ENDBODY;
294
295 $activity = $this->activityCreate([
296 'subject' => 'Important message read immediately!',
297 'duration' => NULL,
298 'location' => NULL,
299 'details' => $messageBody,
300 'status_id' => 'Completed',
301 'activity_type_id' => 'Inbound Email',
302 'source_contact_id' => $this->source,
303 'assignee_contact_id' => NULL,
304 ]);
305 $activity_id = $activity['id'];
306
307 // Simulate viewing it from the form.
308
309 $form = new CRM_Activity_Form_Activity();
310 $form->controller = new CRM_Core_Controller_Simple('CRM_Activity_Form_Activity', 'Activity');
311 $form->set('context', 'standalone');
312 $form->set('cid', $this->source);
313 $form->set('action', 'view');
314 $form->set('id', $activity_id);
315 $form->set('atype', $activity['values'][$activity_id]['activity_type_id']);
316
317 $form->buildForm();
318
319 // Wish there was another way to do this
320 $form->controller->handle($form, 'display');
321
322 // This isn't a faithful representation of the output since there'll
323 // probably be a lot missing, but for now I don't see a simpler way to
324 // do this.
325 // Also this is printing the template code to the console. It doesn't hurt
326 // the test but it's clutter and I don't know where it's coming from
327 // and can't seem to prevent it.
328 $output = $form->getTemplate()->fetch($form->getTemplateFileName());
329
330 // This kind of suffers from the same problem as the old webtests. It's
331 // a bit brittle and tied to the UI.
332 $this->assertContains("Hi,<br />\n<br />\nWassup!?!?<br />\n<br />\nLet's check if the output when viewing the form has legible line breaks in the output.<br />\n<br />\nThanks!", $output);
333
334 // Put label back
335 $this->callAPISuccess('OptionValue', 'create', [
336 'id' => $inbound_email['id'],
337 'label' => $inbound_email['label'],
338 ]);
339 }
340
341 }