dev/core#2814 Fix activity:sendEmail to use renderTemplate
[civicrm-core.git] / tests / phpunit / CRM / Activity / BAO / ActivityTest.php
1 <?php
2
3 use Civi\Api4\Activity;
4
5 /**
6 * Class CRM_Activity_BAO_ActivityTest
7 * @group headless
8 */
9 class CRM_Activity_BAO_ActivityTest extends CiviUnitTestCase {
10
11 private $allowedContactsACL = [];
12
13 private $loggedInUserId = NULL;
14
15 private $someContacts = [];
16
17 /**
18 * Set up for test.
19 *
20 * @throws \CiviCRM_API3_Exception
21 */
22 public function setUp():void {
23 parent::setUp();
24 $this->prepareForACLs();
25 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['view all contacts', 'access CiviCRM'];
26 $this->setupForSmsTests();
27 }
28
29 /**
30 * Clean up after tests.
31 *
32 * @throws \API_Exception
33 * @throws \CRM_Core_Exception
34 * @throws \CiviCRM_API3_Exception
35 */
36 public function tearDown(): void {
37 $tablesToTruncate = [
38 'civicrm_activity',
39 'civicrm_activity_contact',
40 'civicrm_uf_match',
41 'civicrm_campaign',
42 'civicrm_email',
43 ];
44 $this->quickCleanup($tablesToTruncate);
45 $this->cleanUpAfterACLs();
46 $this->setupForSmsTests(TRUE);
47 parent::tearDown();
48 }
49
50 /**
51 * Test case for create() method.
52 *
53 * @throws \CRM_Core_Exception
54 */
55 public function testCreate() {
56 $contactId = $this->individualCreate();
57
58 $params = [
59 'source_contact_id' => $contactId,
60 'subject' => 'Scheduling Meeting',
61 'activity_type_id' => 2,
62 ];
63
64 CRM_Activity_BAO_Activity::create($params);
65
66 $activityId = $this->assertDBNotNull('CRM_Activity_DAO_Activity', 'Scheduling Meeting', 'id',
67 'subject', 'Database check for created activity.'
68 );
69
70 // Now call create() to modify an existing Activity.
71 $params = [
72 'id' => $activityId,
73 'source_contact_id' => $contactId,
74 'subject' => 'Scheduling Interview',
75 'activity_type_id' => 3,
76 ];
77
78 CRM_Activity_BAO_Activity::create($params);
79
80 $activityTypeId = $this->assertDBNotNull('CRM_Activity_DAO_Activity', 'Scheduling Interview',
81 'activity_type_id',
82 'subject', 'Database check on updated activity record.'
83 );
84 $this->assertEquals($activityTypeId, 3, 'Verify activity type id is 3.');
85
86 $this->contactDelete($contactId);
87 }
88
89 /**
90 * Test case for getContactActivity() method.
91 *
92 * getContactActivity() method get activities detail for given target contact
93 * id.
94 *
95 * @throws \CRM_Core_Exception
96 * @throws \CiviCRM_API3_Exception
97 */
98 public function testGetContactActivity() {
99 $contactId = $this->individualCreate();
100 $params = [
101 'first_name' => 'liz',
102 'last_name' => 'hurleey',
103 ];
104 $targetContactId = $this->individualCreate($params);
105
106 $params = [
107 'source_contact_id' => $contactId,
108 'subject' => 'Scheduling Meeting',
109 'activity_type_id' => 2,
110 'target_contact_id' => [$targetContactId],
111 'activity_date_time' => date('Ymd'),
112 ];
113
114 $this->callAPISuccess('Activity', 'create', $params);
115
116 $activityId = $this->assertDBNotNull('CRM_Activity_DAO_Activity', 'Scheduling Meeting',
117 'id',
118 'subject', 'Database check for created activity.'
119 );
120
121 // @todo - remove this deprecated functions
122 $activities = CRM_Activity_BAO_Activity::getContactActivity($targetContactId);
123
124 $this->assertEquals($activities[$activityId]['subject'], 'Scheduling Meeting', 'Verify activity subject is correct.');
125
126 $this->contactDelete($contactId);
127 $this->contactDelete($targetContactId);
128 }
129
130 /**
131 * Test case for retrieve() method.
132 *
133 * Retrieve($params, $defaults) method return activity detail for given params
134 * and set defaults.
135 */
136 public function testRetrieve(): void {
137 $contactId = $this->individualCreate();
138 $params = [
139 'first_name' => 'liz',
140 'last_name' => 'hurleey',
141 ];
142 $targetContactId = $this->individualCreate($params);
143
144 $params = [
145 'source_contact_id' => $contactId,
146 'subject' => 'Scheduling Meeting',
147 'activity_type_id' => 2,
148 'target_contact_id' => [$targetContactId],
149 'activity_date_time' => date('Ymd'),
150 ];
151
152 CRM_Activity_BAO_Activity::create($params);
153
154 $this->assertDBNotNull('CRM_Activity_DAO_Activity', 'Scheduling Meeting', 'id',
155 'subject', 'Database check for created activity.'
156 );
157
158 $this->assertDBNotNull('CRM_Activity_DAO_ActivityContact', $targetContactId,
159 'id', 'contact_id',
160 'Database check for created activity target.'
161 );
162
163 $defaults = [];
164 $activity = CRM_Activity_BAO_Activity::retrieve($params, $defaults);
165
166 $this->assertEquals($activity->subject, 'Scheduling Meeting', 'Verify activity subject is correct.');
167 $this->assertEquals($activity->activity_type_id, 2, 'Verify activity type id is correct.');
168 $this->assertEquals($defaults['source_contact_id'], $contactId, 'Verify source contact id is correct.');
169
170 $this->assertEquals($defaults['subject'], 'Scheduling Meeting', 'Verify activity subject is correct.');
171 $this->assertEquals($defaults['activity_type_id'], 2, 'Verify activity type id is correct.');
172
173 $this->assertEquals($defaults['target_contact'][0], $targetContactId, 'Verify target contact id is correct.');
174
175 $this->contactDelete($contactId);
176 $this->contactDelete($targetContactId);
177 }
178
179 /**
180 * Test Assigning a target contact but then the logged in user cannot see the contact
181 */
182 public function testTargetContactNotavaliable() {
183 $contactId = $this->individualCreate();
184 $params = [
185 'first_name' => 'liz',
186 'last_name' => 'hurleey',
187 ];
188 $targetContactId = $this->individualCreate($params);
189
190 $params = [
191 'source_contact_id' => $contactId,
192 'subject' => 'Scheduling Meeting',
193 'activity_type_id' => 2,
194 'target_contact_id' => [$targetContactId],
195 'activity_date_time' => date('Ymd'),
196 ];
197
198 CRM_Activity_BAO_Activity::create($params);
199
200 // set custom hook
201 $this->hookClass->setHook('civicrm_aclWhereClause', [$this, 'hook_civicrm_aclWhereClause']);
202
203 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
204
205 $this->allowedContactsACL = [$contactId];
206
207 // get logged in user
208 $user_id = $this->createLoggedInUser();
209 $activityGetParams = CRM_Core_Page_AJAX::defaultSortAndPagerParams();
210 $activityGetParams += ['contact_id' => $contactId];
211 $activities = CRM_Activity_BAO_Activity::getContactActivitySelector($activityGetParams);
212 // Aseert that we have sensible data to display in the contact tab
213 $this->assertEquals('Anderson, Anthony', $activities['data'][0]['source_contact_name']);
214 // Note that becasue there is a target contact but it is not accessable the output is an empty string not n/a
215 $this->assertEquals('', $activities['data'][0]['target_contact_name']);
216 // verify that doing the underlying query shows we get a target contact_id
217 $this->assertEquals(1, CRM_Activity_BAO_Activity::getActivities(['contact_id' => $contactId])[1]['target_contact_count']);
218 $this->cleanUpAfterACLs();
219 }
220
221 /**
222 * Check for errors when viewing a contact's activity tab when there
223 * is an activity that doesn't have a target (With Contact).
224 */
225 public function testActivitySelectorNoTargets() {
226 $contact_id = $this->individualCreate([], 0, TRUE);
227 $activity = $this->callAPISuccess('activity', 'create', [
228 'source_contact_id' => $contact_id,
229 'activity_type_id' => 'Meeting',
230 'subject' => 'Lonely Meeting',
231 'details' => 'Here at this meeting all by myself and no other contacts.',
232 ]);
233 $input = [
234 '_raw_values' => [],
235 'offset' => 0,
236 'rp' => 25,
237 'page' => 1,
238 'context' => 'activity',
239 'contact_id' => $contact_id,
240 ];
241 $output = CRM_Activity_BAO_Activity::getContactActivitySelector($input);
242 $this->assertEquals($activity['id'], $output['data'][0]['DT_RowId']);
243 $this->assertEquals('<em>n/a</em>', $output['data'][0]['target_contact_name']);
244 $this->assertEquals('Lonely Meeting', $output['data'][0]['subject']);
245
246 $this->callAPISuccess('activity', 'delete', ['id' => $activity['id']]);
247 $this->callAPISuccess('contact', 'delete', ['id' => $contact_id]);
248 }
249
250 /**
251 * Test case for deleteActivity() method.
252 *
253 * deleteActivity($params) method deletes activity for given params.
254 *
255 * @throws \CRM_Core_Exception
256 * @throws \CiviCRM_API3_Exception
257 */
258 public function testDeleteActivity() {
259 $contactId = $this->individualCreate();
260 $params = [
261 'first_name' => 'liz',
262 'last_name' => 'hurleey',
263 ];
264 $targetContactId = $this->individualCreate($params);
265
266 $params = [
267 'source_contact_id' => $contactId,
268 'source_record_id' => $contactId,
269 'subject' => 'Scheduling Meeting',
270 'activity_type_id' => 2,
271 'target_contact_id' => [$targetContactId],
272 'activity_date_time' => date('Ymd'),
273 ];
274
275 CRM_Activity_BAO_Activity::create($params);
276
277 $this->assertDBNotNull('CRM_Activity_DAO_Activity', 'Scheduling Meeting', 'id',
278 'subject', 'Database check for created activity.'
279 );
280
281 $this->assertDBNotNull('CRM_Activity_DAO_ActivityContact', $targetContactId,
282 'id', 'contact_id',
283 'Database check for created activity target.'
284 );
285
286 $paramOptions = ['0))+and+0+--+-f', ['0))+and+0+--+-f']];
287 $paramField = ['source_record_id', 'activity_type_id'];
288 foreach ($paramField as $field) {
289 foreach ($paramOptions as $paramOption) {
290 $params = [
291 $field => $paramOption,
292 ];
293 try {
294 CRM_Activity_BAO_Activity::deleteActivity($params);
295 }
296 catch (Exception $e) {
297 if ($e->getMessage() === 'DB Error: syntax error') {
298 $this->fail('Delete Activity function did not validate field: ' . $field);
299 }
300 }
301 }
302 }
303 $params = [
304 'source_contact_id' => $contactId,
305 'source_record_id' => $contactId,
306 'subject' => 'Scheduling Meeting',
307 'activity_type_id' => 2,
308 ];
309
310 CRM_Activity_BAO_Activity::deleteActivity($params);
311
312 $this->assertDBNull('CRM_Activity_DAO_Activity', 'Scheduling Meeting', 'id',
313 'subject', 'Database check for deleted activity.'
314 );
315 $this->contactDelete($contactId);
316 $this->contactDelete($targetContactId);
317 }
318
319 /**
320 * Test case for deleteActivityContact() method.
321 */
322 public function testDeleteActivityTarget() {
323 $contactId = $this->individualCreate();
324 $params = [
325 'first_name' => 'liz',
326 'last_name' => 'hurleey',
327 ];
328 $targetContactId = $this->individualCreate($params);
329
330 $params = [
331 'source_contact_id' => $contactId,
332 'subject' => 'Scheduling Meeting',
333 'activity_type_id' => 2,
334 'target_contact_id' => [$targetContactId],
335 'activity_date_time' => date('Ymd'),
336 ];
337
338 CRM_Activity_BAO_Activity::create($params);
339
340 $activityId = $this->assertDBNotNull('CRM_Activity_DAO_Activity', 'Scheduling Meeting', 'id',
341 'subject', 'Database check for created activity.'
342 );
343
344 $this->assertDBNotNull('CRM_Activity_DAO_ActivityContact', $targetContactId,
345 'id', 'contact_id',
346 'Database check for created activity target.'
347 );
348
349 CRM_Activity_BAO_Activity::deleteActivityContact($activityId, 3);
350
351 $this->assertDBNull('CRM_Activity_DAO_ActivityContact', $targetContactId, 'id',
352 'contact_id', 'Database check for deleted activity target.'
353 );
354
355 $this->contactDelete($contactId);
356 $this->contactDelete($targetContactId);
357 }
358
359 /**
360 * Test case for deleteActivityAssignment() method.
361 *
362 * deleteActivityAssignment($activityId) method deletes activity assignment
363 * for given activity id.
364 *
365 * @throws \CRM_Core_Exception
366 * @throws \CiviCRM_API3_Exception
367 */
368 public function testDeleteActivityAssignment(): void {
369 $contactId = $this->individualCreate();
370 $params = [
371 'first_name' => 'liz',
372 'last_name' => 'hurleey',
373 ];
374 $assigneeContactId = $this->individualCreate($params);
375
376 $params = [
377 'source_contact_id' => $contactId,
378 'subject' => 'Scheduling Meeting',
379 'activity_type_id' => 2,
380 'assignee_contact_id' => [$assigneeContactId],
381 'activity_date_time' => date('Ymd'),
382 ];
383
384 CRM_Activity_BAO_Activity::create($params);
385
386 $activityId = $this->assertDBNotNull('CRM_Activity_DAO_Activity', 'Scheduling Meeting', 'id',
387 'subject', 'Database check for created activity.'
388 );
389
390 $this->assertDBNotNull('CRM_Activity_DAO_ActivityContact',
391 $assigneeContactId, 'id', 'contact_id',
392 'Database check for created activity assignment.'
393 );
394
395 CRM_Activity_BAO_Activity::deleteActivityContact($activityId, 1);
396
397 $this->assertDBNull('CRM_Activity_DAO_ActivityContact', $assigneeContactId, 'id',
398 'contact_id', 'Database check for deleted activity assignment.'
399 );
400
401 $this->contactDelete($contactId);
402 $this->contactDelete($assigneeContactId);
403 }
404
405 /**
406 * Test getActivities BAO method for getting count.
407 *
408 */
409 public function testGetActivitiesCountForAdminDashboard() {
410 // Reset to default
411 $this->setShowCaseActivitiesInCore(FALSE);
412 $this->setUpForActivityDashboardTests();
413 $this->addCaseWithActivity();
414 CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'access all cases and activities';
415
416 $activityCount = CRM_Activity_BAO_Activity::getActivitiesCount($this->_params);
417 $this->assertEquals(8, $activityCount);
418
419 // If we're showing case activities, we exepct to see one more (the scheduled meeting)...
420 $this->setShowCaseActivitiesInCore(TRUE);
421 $activityCount = CRM_Activity_BAO_Activity::getActivitiesCount($this->_params);
422 $this->assertEquals(9, $activityCount);
423 // Reset to default
424 $this->setShowCaseActivitiesInCore(FALSE);
425 }
426
427 /**
428 * Test getActivities BAO method for getting count
429 *
430 */
431 public function testGetActivitiesCountforNonAdminDashboard() {
432 // Reset to default
433 $this->setShowCaseActivitiesInCore(FALSE);
434 $this->createTestActivities();
435 $this->addCaseWithActivity();
436 CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'access all cases and activities';
437
438 $params = [
439 'contact_id' => 9,
440 'admin' => FALSE,
441 'caseId' => NULL,
442 'context' => 'home',
443 'activity_type_id' => NULL,
444 // for dashlet the Scheduled status is set by default
445 'activity_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Scheduled'),
446 'offset' => 0,
447 'rowCount' => 0,
448 'sort' => NULL,
449 ];
450
451 //since we are loading activities from dataset, we know total number of activities for this contact
452 // 5 activities ( 2 scheduled, 3 Completed, 1 Scheduled Case activity ),
453 // note that dashboard shows only scheduled activities
454 $this->assertEquals(2, CRM_Activity_BAO_Activity::getActivitiesCount($params));
455
456 // If we're showing case activities, we expect to see one more (the scheduled meeting)...
457 $this->setShowCaseActivitiesInCore(TRUE);
458 $this->assertEquals(3, CRM_Activity_BAO_Activity::getActivitiesCount($params));
459 // Reset to default
460 $this->setShowCaseActivitiesInCore(FALSE);
461 }
462
463 /**
464 * Test getActivities BAO method for getting count
465 *
466 */
467 public function testGetActivitiesCountforContactSummary() {
468 // Reset to default
469 $this->setShowCaseActivitiesInCore(FALSE);
470 $this->createTestActivities();
471 $this->addCaseWithActivity();
472 CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'access all cases and activities';
473
474 $params = [
475 'contact_id' => 9,
476 'admin' => FALSE,
477 'caseId' => NULL,
478 'context' => 'activity',
479 'activity_type_id' => NULL,
480 'offset' => 0,
481 'rowCount' => 0,
482 'sort' => NULL,
483 ];
484
485 //since we are loading activities from dataset, we know total number of activities for this contact
486 // 5 activities
487 $this->assertEquals(5, CRM_Activity_BAO_Activity::getActivitiesCount($params));
488
489 // If we're showing case activities, we exepct to see one more (the scheduled meeting)...
490 $this->setShowCaseActivitiesInCore(TRUE);
491 $this->assertEquals(6, CRM_Activity_BAO_Activity::getActivitiesCount($params));
492 // Reset to default
493 $this->setShowCaseActivitiesInCore(FALSE);
494 }
495
496 /**
497 * CRM-18706 - Test Include/Exclude Activity Filters
498 */
499 public function testActivityFilters() {
500 $this->createTestActivities();
501 Civi::settings()->set('preserve_activity_tab_filter', 1);
502 $this->createLoggedInUser();
503
504 global $_GET;
505 $_GET = [
506 'cid' => 9,
507 'context' => 'activity',
508 'activity_type_id' => 1,
509 ];
510 $expectedFilters = [
511 'activity_type_filter_id' => 1,
512 ];
513
514 try {
515 CRM_Activity_Page_AJAX::getContactActivity();
516 }
517 catch (CRM_Core_Exception_PrematureExitException $e) {
518 $activityFilter = Civi::contactSettings()->get('activity_tab_filter');
519 $activities = $e->errorData;
520 }
521 //Assert whether filters are correctly set.
522 $this->checkArrayEquals($expectedFilters, $activityFilter);
523 // This should include activities of type Meeting only.
524 foreach ($activities['data'] as $value) {
525 $this->assertStringContainsString('Meeting', $value['activity_type']);
526 }
527 unset($_GET['activity_type_id']);
528
529 $_GET['activity_type_exclude_id'] = $expectedFilters['activity_type_exclude_filter_id'] = 1;
530 try {
531 CRM_Activity_Page_AJAX::getContactActivity();
532 }
533 catch (CRM_Core_Exception_PrematureExitException $e) {
534 $activityFilter = Civi::contactSettings()->get('activity_tab_filter');
535 $activities = $e->errorData;
536 }
537 $this->assertEquals(['activity_type_exclude_filter_id' => 1], $activityFilter);
538 // None of the activities should be of type Meeting.
539 foreach ($activities['data'] as $value) {
540 $this->assertStringNotContainsString('Meeting', $value['activity_type']);
541 }
542 }
543
544 /**
545 * Test getActivities BAO method for getting count
546 */
547 public function testGetActivitiesCountforContactSummaryWithNoActivities() {
548 $this->createTestActivities();
549
550 $params = [
551 'contact_id' => 17,
552 'admin' => FALSE,
553 'caseId' => NULL,
554 'context' => 'home',
555 'activity_type_id' => NULL,
556 'offset' => 0,
557 'rowCount' => 0,
558 'sort' => NULL,
559 ];
560
561 //since we are loading activities from dataset, we know total number of activities for this contact
562 // this contact does not have any activity
563 $this->assertEquals(0, CRM_Activity_BAO_Activity::getActivitiesCount($params));
564 }
565
566 /**
567 * Test getActivities BAO method.
568 */
569 public function testGetActivitiesForAdminDashboard() {
570 $this->setShowCaseActivitiesInCore(FALSE);
571 $this->setUpForActivityDashboardTests();
572 $this->addCaseWithActivity();
573 CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'access all cases and activities';
574
575 $activitiesNew = CRM_Activity_BAO_Activity::getActivities($this->_params);
576 // $this->assertEquals($activities, $activitiesDeprecatedFn);
577
578 //since we are loading activities from dataset, we know total number of activities
579 // with no contact ID and there should be 8 schedule activities shown on dashboard
580 $count = 8;
581 foreach ([$activitiesNew] as $activities) {
582 $this->assertCount($count, $activities);
583
584 foreach ($activities as $key => $value) {
585 $this->assertEquals($value['subject'], "subject {$key}", 'Verify activity subject is correct.');
586 $this->assertEquals($value['activity_type_id'], 2, 'Verify activity type is correct.');
587 $this->assertEquals($value['status_id'], 1, 'Verify all activities are scheduled.');
588 }
589 }
590
591 // Now check that we get the scheduled meeting, if civicaseShowCaseActivities is set.
592 $this->setShowCaseActivitiesInCore(TRUE);
593 $activitiesNew = CRM_Activity_BAO_Activity::getActivities($this->_params);
594 $this->assertCount(9, $activitiesNew);
595 // Scan through to find the meeting.
596 $this->assertContains('test meeting activity', array_column($activitiesNew, 'subject'), "failed to find scheduled case Meeting activity");
597 // Reset to default
598 $this->setShowCaseActivitiesInCore(FALSE);
599 }
600
601 /**
602 * Test getActivities BAO method.
603 */
604 public function testGetActivitiesForAdminDashboardNoViewContacts() {
605 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
606 $this->setUpForActivityDashboardTests();
607 foreach ([CRM_Activity_BAO_Activity::getActivities($this->_params)] as $activities) {
608 // Skipped until we get back to the upgraded version properly.
609 $this->assertCount(0, $activities);
610 }
611 }
612
613 /**
614 * Test getActivities BAO method.
615 */
616 public function testGetActivitiesForAdminDashboardAclLimitedViewContacts() {
617 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
618 $this->allowedContacts = [1, 3, 4, 5];
619 $this->hookClass->setHook('civicrm_aclWhereClause', [$this, 'aclWhereMultipleContacts']);
620 $this->setUpForActivityDashboardTests();
621 $this->assertEquals(7, count(CRM_Activity_BAO_Activity::getActivities($this->_params)));
622 }
623
624 /**
625 * Test getActivities BAO method.
626 */
627 public function testGetActivitiesforNonAdminDashboard() {
628 $this->setShowCaseActivitiesInCore(FALSE);
629 $this->createTestActivities();
630 $this->addCaseWithActivity();
631 CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'access all cases and activities';
632
633 $contactID = 9;
634 $params = [
635 'contact_id' => $contactID,
636 'admin' => FALSE,
637 'caseId' => NULL,
638 'context' => 'home',
639 'activity_type_id' => NULL,
640 // for dashlet the Scheduled status is set by default
641 'activity_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Scheduled'),
642 'offset' => 0,
643 'rowCount' => 0,
644 'sort' => NULL,
645 ];
646
647 foreach ([CRM_Activity_BAO_Activity::getActivities($params)] as $activities) {
648 //since we are loading activities from dataset, we know total number of activities for this contact
649 // 5 activities ( 2 scheduled, 3 Completed ), note that dashboard shows only scheduled activities
650 $count = 2;
651 $this->assertEquals($count, count($activities));
652
653 foreach ($activities as $key => $value) {
654 $this->assertEquals($value['subject'], "subject {$key}", 'Verify activity subject is correct.');
655 $this->assertEquals($value['activity_type_id'], 2, 'Verify activity type is correct.');
656 $this->assertEquals($value['status_id'], 1, 'Verify all activities are scheduled.');
657
658 if ($key == 3) {
659 $this->assertArrayHasKey($contactID, $value['target_contact_name']);
660 }
661 elseif ($key == 4) {
662 $this->assertArrayHasKey($contactID, $value['assignee_contact_name']);
663 }
664 }
665 }
666
667 // Now check that we get the scheduled meeting, if civicaseShowCaseActivities is set.
668 $this->setShowCaseActivitiesInCore(TRUE);
669 $activities = CRM_Activity_BAO_Activity::getActivities($params);
670 $this->assertEquals(3, count($activities));
671 // Scan through to find the meeting.
672 $this->assertTrue(in_array('test meeting activity', array_column($activities, 'subject')),
673 "failed to find scheduled case Meeting activity");
674
675 // Reset to default
676 $this->setShowCaseActivitiesInCore(FALSE);
677 }
678
679 /**
680 * Test target contact count.
681 */
682 public function testTargetCountforContactSummary() {
683 $targetCount = 5;
684 $contactId = $this->individualCreate();
685 $targetContactIDs = [];
686 for ($i = 0; $i < $targetCount; $i++) {
687 $targetContactIDs[] = $this->individualCreate([], $i);
688 }
689 // Create activities with 5 target contacts.
690 $activityParams = [
691 'source_contact_id' => $contactId,
692 'target_contact_id' => $targetContactIDs,
693 ];
694 $this->activityCreate($activityParams);
695
696 $params = [
697 'contact_id' => $contactId,
698 'context' => 'activity',
699 ];
700 $activities = CRM_Activity_BAO_Activity::getActivities($params);
701 //verify target count
702 $this->assertEquals($targetCount, $activities[1]['target_contact_count']);
703 $this->assertEquals([$targetContactIDs[0] => 'Anderson, Anthony'], $activities[1]['target_contact_name']);
704 $this->assertEquals('Anderson, Anthony', $activities[1]['source_contact_name']);
705 $this->assertEquals('Anderson, Anthony', $activities[1]['assignee_contact_name'][4]);
706 }
707
708 /**
709 * Test getActivities BAO method.
710 */
711 public function testGetActivitiesforContactSummaryWithSortOptions() {
712 $this->createTestActivities();
713 $params = [
714 'contact_id' => 9,
715 'admin' => FALSE,
716 'caseId' => NULL,
717 'context' => 'activity',
718 'activity_type_id' => NULL,
719 'offset' => 0,
720 'rowCount' => 0,
721 'sort' => 'source_contact_name desc',
722 ];
723
724 $activities = CRM_Activity_BAO_Activity::getActivities($params);
725 $alphaOrder = ['Test Contact 11', 'Test Contact 12', 'Test Contact 3', 'Test Contact 4', 'Test Contact 9'];
726 foreach ($activities as $activity) {
727 $this->assertEquals(array_pop($alphaOrder), $activity['source_contact_name']);
728 }
729
730 }
731
732 /**
733 * Test getActivities BAO method.
734 */
735 public function testGetActivitiesForContactSummary() {
736 // Reset to default
737 $this->setShowCaseActivitiesInCore(FALSE);
738 $this->createTestActivities();
739 $this->addCaseWithActivity();
740 CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'access all cases and activities';
741
742 $contactID = 9;
743 $params = [
744 'contact_id' => $contactID,
745 'admin' => FALSE,
746 'caseId' => NULL,
747 'context' => 'activity',
748 'activity_type_id' => NULL,
749 'offset' => 0,
750 'rowCount' => 0,
751 ];
752
753 //since we are loading activities from dataset, we know total number of activities for this contact
754 // 5 activities, Contact Summary should show all activities
755 $count = 5;
756 $activities = CRM_Activity_BAO_Activity::getActivities($params);
757 $this->assertEquals($count, count($activities));
758 foreach ($activities as $key => $value) {
759 $this->assertEquals($value['subject'], "subject {$key}", 'Verify activity subject is correct.');
760
761 if ($key > 8) {
762 $this->assertEquals($value['status_id'], 2, 'Verify all activities are scheduled.');
763 }
764 else {
765 $this->assertEquals($value['status_id'], 1, 'Verify all activities are scheduled.');
766 }
767
768 if ($key === 12) {
769 $this->assertEquals($value['activity_type'], 'Bulk Email', 'Verify activity type is correct.');
770 $this->assertEquals('(2 recipients)', $value['recipients']);
771 $targetContactID = key($value['target_contact_name']);
772 // The 2 targets have ids 10 & 11. Since they are not sorted it could be either on some systems.
773 $this->assertTrue(in_array($targetContactID, [10, 11]));
774 }
775 elseif ($key > 8) {
776 $this->assertEquals($value['activity_type_id'], 1, 'Verify activity type is correct.');
777 }
778 else {
779 $this->assertEquals($value['activity_type_id'], 2, 'Verify activity type is correct.');
780 }
781
782 if ($key == 3) {
783 $this->assertEquals([$contactID => 'Test Contact ' . $contactID], $value['target_contact_name']);
784 }
785 elseif ($key == 4) {
786 $this->assertArrayHasKey($contactID, $value['assignee_contact_name']);
787 }
788 }
789
790 // Now check that we get the scheduled meeting, if civicaseShowCaseActivities is set.
791 $this->setShowCaseActivitiesInCore(TRUE);
792 $activities = CRM_Activity_BAO_Activity::getActivities($params);
793 $this->assertEquals(6, count($activities));
794 // Scan through to find the meeting.
795 $this->assertTrue(in_array('test meeting activity', array_column($activities, 'subject')),
796 "failed to find scheduled case Meeting activity");
797 // Reset to default
798 $this->setShowCaseActivitiesInCore(FALSE);
799 }
800
801 /**
802 * Test getActivities BAO method.
803 */
804 public function testGetActivitiesforContactSummaryWithActivities() {
805 // Reset to default
806 $this->setShowCaseActivitiesInCore(FALSE);
807 $this->createTestActivities();
808
809 // parameters for different test cases, check each array key for the specific test-case
810 $testCases = [
811 'with-no-activity' => [
812 'params' => [
813 'contact_id' => 17,
814 'admin' => FALSE,
815 'caseId' => NULL,
816 'context' => 'home',
817 'activity_type_id' => NULL,
818 'offset' => 0,
819 'rowCount' => 0,
820 'sort' => NULL,
821 ],
822 ],
823 'with-activity' => [
824 'params' => [
825 'contact_id' => 1,
826 'admin' => FALSE,
827 'caseId' => NULL,
828 'context' => 'home',
829 'activity_type_id' => NULL,
830 'offset' => 0,
831 'rowCount' => 0,
832 'sort' => NULL,
833 ],
834 ],
835 'with-activity_type' => [
836 'params' => [
837 'contact_id' => 3,
838 'admin' => FALSE,
839 'caseId' => NULL,
840 'context' => 'home',
841 'activity_type_id' => 2,
842 'offset' => 0,
843 'rowCount' => 0,
844 'sort' => NULL,
845 ],
846 ],
847 'exclude-all-activity_type' => [
848 'params' => [
849 'contact_id' => 3,
850 'admin' => FALSE,
851 'caseId' => NULL,
852 'context' => 'home',
853 'activity_type_exclude_id' => [1, 2],
854 'offset' => 0,
855 'rowCount' => 0,
856 'sort' => NULL,
857 ],
858 ],
859 'sort-by-subject' => [
860 'params' => [
861 'contact_id' => 1,
862 'admin' => FALSE,
863 'caseId' => NULL,
864 'context' => 'home',
865 'activity_type_id' => NULL,
866 'offset' => 0,
867 'rowCount' => 0,
868 'sort' => 'subject DESC',
869 ],
870 ],
871 ];
872
873 foreach ($testCases as $caseName => $testCase) {
874 $activityCount = CRM_Activity_BAO_Activity::getActivitiesCount($testCase['params']);
875 $activitiesNew = CRM_Activity_BAO_Activity::getActivities($testCase['params']);
876
877 foreach ([$activitiesNew] as $activities) {
878 //$this->assertEquals($activityCount, CRM_Activity_BAO_Activity::getActivitiesCount($testCase['params']));
879 if ($caseName === 'with-no-activity') {
880 $this->assertEquals(0, count($activities));
881 $this->assertEquals(0, $activityCount);
882 }
883 elseif ($caseName === 'with-activity') {
884 // contact id 1 is assigned as source, target and assignee for activity id 1, 7 and 8 respectively
885 $this->assertEquals(3, count($activities));
886 $this->assertEquals(3, $activityCount);
887 $this->assertEquals(1, $activities[1]['source_contact_id']);
888 // @todo - this is a discrepancy between old & new - review.
889 //$this->assertEquals(TRUE, array_key_exists(1, $activities[7]['target_contact_name']));
890 $this->assertEquals(TRUE, array_key_exists(1, $activities[8]['assignee_contact_name']));
891 }
892 elseif ($caseName === 'with-activity_type') {
893 // contact id 3 for activity type 2 is assigned as assignee, source and target for
894 // activity id 1, 3 and 8 respectively
895 $this->assertEquals(3, count($activities));
896 $this->assertEquals(3, $activityCount);
897 // ensure activity type id is 2
898 $this->assertEquals(2, $activities[1]['activity_type_id']);
899 $this->assertEquals(3, $activities[3]['source_contact_id']);
900 // @todo review inconsistency between 2 versions.
901 // $this->assertEquals(TRUE, array_key_exists(3, $activities[8]['target_contact_name']));
902 $this->assertEquals(TRUE, array_key_exists(3, $activities[1]['assignee_contact_name']));
903 }
904 if ($caseName === 'exclude-all-activity_type') {
905 $this->assertEquals(0, count($activities));
906 $this->assertEquals(0, $activityCount);
907 }
908 if ($caseName === 'sort-by-subject') {
909 $this->assertEquals(3, count($activities));
910 $this->assertEquals(3, $activityCount);
911 // activities should be order by 'subject DESC'
912 $subjectOrder = [
913 'subject 8',
914 'subject 7',
915 'subject 1',
916 ];
917 $count = 0;
918 foreach ($activities as $activity) {
919 $this->assertEquals($subjectOrder[$count], $activity['subject']);
920 $count++;
921 }
922 }
923 }
924 }
925 }
926
927 /**
928 * CRM-20793 : Test getActivities by using activity date and status filter
929 *
930 * @throws \CRM_Core_Exception
931 */
932 public function testByActivityDateAndStatus(): void {
933 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['view all contacts', 'access CiviCRM'];
934 $this->createTestActivities();
935
936 // activity IDs catagorised by date
937 $lastWeekActivities = [1, 2, 3];
938 $todayActivities = [4, 5, 6, 7];
939 $lastTwoMonthsActivities = [8, 9, 10, 11];
940 $lastOrNextYearActivities = [12, 13, 14, 15, 16];
941
942 // date values later used to set activity date value
943 $lastWeekDate = date('YmdHis', strtotime('1 week ago'));
944 $today = date('YmdHis');
945 $lastTwoMonthAgoDate = date('YmdHis', strtotime('2 months ago'));
946 // if current month is Jan then choose next year date otherwise the search result will include
947 // the previous week and last two months activities which are still in previous year and hence leads to improper result
948 $lastOrNextYearDate = (date('M') === 'Jan') ? date('YmdHis', strtotime('+1 year')) : date('YmdHis', strtotime('1 year ago'));
949 for ($i = 1; $i <= 16; $i++) {
950 if (in_array($i, $lastWeekActivities)) {
951 $date = $lastWeekDate;
952 }
953 elseif (in_array($i, $lastTwoMonthsActivities)) {
954 $date = $lastTwoMonthAgoDate;
955 }
956 elseif (in_array($i, $lastOrNextYearActivities)) {
957 $date = $lastOrNextYearDate;
958 }
959 elseif (in_array($i, $todayActivities)) {
960 $date = $today;
961 }
962 $this->callAPISuccess('Activity', 'create', [
963 'id' => $i,
964 'activity_date_time' => $date,
965 ]);
966 }
967
968 // parameters for different test cases, check each array key for the specific test-case
969 $testCases = [
970 'todays-activity' => [
971 'params' => [
972 'contact_id' => 1,
973 'admin' => TRUE,
974 'caseId' => NULL,
975 'context' => 'activity',
976 'activity_date_time_relative' => 'this.day',
977 'activity_type_id' => NULL,
978 'offset' => 0,
979 'rowCount' => 0,
980 'sort' => NULL,
981 ],
982 ],
983 'todays-activity-filtered-by-range' => [
984 'params' => [
985 'contact_id' => 1,
986 'admin' => TRUE,
987 'caseId' => NULL,
988 'context' => 'activity',
989 'activity_date_time_low' => date('Y/m/d', strtotime('yesterday')),
990 'activity_date_time_high' => date('Y/m/d'),
991 'activity_type_id' => NULL,
992 'offset' => 0,
993 'rowCount' => 0,
994 'sort' => NULL,
995 ],
996 ],
997 'last-week-activity' => [
998 'params' => [
999 'contact_id' => 1,
1000 'admin' => TRUE,
1001 'caseId' => NULL,
1002 'context' => 'activity',
1003 'activity_date_time_relative' => 'previous.week',
1004 'activity_type_id' => NULL,
1005 'offset' => 0,
1006 'rowCount' => 0,
1007 'sort' => NULL,
1008 ],
1009 ],
1010 'this-quarter-activity' => [
1011 'params' => [
1012 'contact_id' => 1,
1013 'admin' => TRUE,
1014 'caseId' => NULL,
1015 'context' => 'activity',
1016 'activity_date_time_relative' => 'this.quarter',
1017 'activity_type_id' => NULL,
1018 'offset' => 0,
1019 'rowCount' => 0,
1020 'sort' => NULL,
1021 ],
1022 ],
1023 'activity-of-all-statuses' => [
1024 'params' => [
1025 'contact_id' => 1,
1026 'admin' => TRUE,
1027 'caseId' => NULL,
1028 'context' => 'activity',
1029 'activity_status_id' => '1,2',
1030 'activity_type_id' => NULL,
1031 'offset' => 0,
1032 'rowCount' => 0,
1033 'sort' => NULL,
1034 ],
1035 ],
1036 ];
1037
1038 foreach ($testCases as $caseName => $testCase) {
1039 CRM_Utils_Date::convertFormDateToApiFormat($testCase['params'], 'activity_date_time', FALSE);
1040 $activities = CRM_Activity_BAO_Activity::getActivities($testCase['params']);
1041 $activityCount = CRM_Activity_BAO_Activity::getActivitiesCount($testCase['params']);
1042 asort($activities);
1043 $activityIDs = array_keys($activities);
1044
1045 if ($caseName === 'todays-activity' || $caseName === 'todays-activity-filtered-by-range') {
1046 // Only one of the 4 activities today relates to contact id 1.
1047 $this->assertEquals(1, $activityCount);
1048 $this->assertEquals(1, count($activities));
1049 $this->assertEquals([7], array_keys($activities));
1050 }
1051 elseif ($caseName === 'last-week-activity') {
1052 // Only one of the 3 activities today relates to contact id 1.
1053 $this->assertEquals(1, $activityCount);
1054 $this->assertEquals(1, count($activities));
1055 $this->assertEquals([1], $activityIDs);
1056 }
1057 elseif ($caseName === 'lhis-quarter-activity') {
1058 $this->assertEquals(count($lastTwoMonthsActivities), $activityCount);
1059 $this->assertEquals(count($lastTwoMonthsActivities), count($activities));
1060 $this->checkArrayEquals($lastTwoMonthsActivities, $activityIDs);
1061 }
1062 elseif ($caseName === 'last-or-next-year-activity') {
1063 $this->assertEquals(count($lastOrNextYearActivities), $activityCount);
1064 $this->assertEquals(count($lastOrNextYearActivities), count($activities));
1065 $this->checkArrayEquals($lastOrNextYearActivities, $activityIDs);
1066 }
1067 elseif ($caseName === 'activity-of-all-statuses') {
1068 $this->assertEquals(3, $activityCount);
1069 $this->assertEquals(3, count($activities));
1070 }
1071 }
1072 }
1073
1074 /**
1075 * @dataProvider getActivityDateData
1076 *
1077 * @param $params
1078 * @param $expected
1079 *
1080 * @throws \CRM_Core_Exception
1081 * @throws \CiviCRM_API3_Exception
1082 */
1083 public function testActivityRelativeDateFilter($params, $expected): void {
1084 $thisYear = date('Y');
1085 $dates = [
1086 date('Y-m-d', strtotime(($thisYear - 1) . '-01-01')),
1087 date('Y-m-d', strtotime(($thisYear - 1) . '-12-31')),
1088 date('Y-m-d', strtotime($thisYear . '-01-01')),
1089 date('Y-m-d', strtotime($thisYear . '-12-31')),
1090 date('Y-m-d', strtotime(($thisYear + 1) . '-01-01')),
1091 date('Y-m-d', strtotime(($thisYear + 1) . '-12-31')),
1092 ];
1093 foreach ($dates as $date) {
1094 $this->activityCreate(['activity_date_time' => $date]);
1095 }
1096 $activitiesDep = CRM_Activity_BAO_Activity::getActivities($params);
1097 $activityCount = CRM_Activity_BAO_Activity::getActivitiesCount($params);
1098 $this->assertEquals(count($activitiesDep), $activityCount);
1099 foreach ($activitiesDep as $activity) {
1100 $this->assertTrue(strtotime($activity['activity_date_time']) >= $expected['earliest'], $activity['activity_date_time'] . ' should be no earlier than ' . date('Y-m-d H:i:s', $expected['earliest']));
1101 $this->assertTrue(strtotime($activity['activity_date_time']) < $expected['latest'], $activity['activity_date_time'] . ' should be before ' . date('Y-m-d H:i:s', $expected['latest']));
1102 }
1103
1104 }
1105
1106 /**
1107 * Get activity date data.
1108 *
1109 * Later we might migrate rework the rest of
1110 * testByActivityDateAndStatus
1111 * to use data provider methodology as it's way complex!
1112 *
1113 * @return array
1114 */
1115 public function getActivityDateData() {
1116 return [
1117 'last-year-activity' => [
1118 'params' => [
1119 'contact_id' => 1,
1120 'admin' => TRUE,
1121 'caseId' => NULL,
1122 'context' => 'activity',
1123 'activity_date_relative' => 'previous.year',
1124 'activity_type_id' => NULL,
1125 'offset' => 0,
1126 'rowCount' => 0,
1127 'sort' => NULL,
1128 ],
1129 'expected' => [
1130 'count' => 2,
1131 'earliest' => strtotime('first day of january last year'),
1132 'latest' => strtotime('first day of january this year'),
1133 ],
1134 ],
1135 ];
1136 }
1137
1138 /**
1139 * CRM-20308: Test from email address when a 'copy of Activity' event occur
1140 *
1141 * @throws \CRM_Core_Exception
1142 * @throws \CiviCRM_API3_Exception
1143 */
1144 public function testEmailAddressOfActivityCopy() {
1145 // Case 1: assert the 'From' Email Address of source Actvity Contact ID
1146 // create activity with source contact ID which has email address
1147 $assigneeContactId = $this->individualCreate();
1148 $sourceContactParams = [
1149 'first_name' => 'liz',
1150 'last_name' => 'hurleey',
1151 'email' => 'liz@testemail.com',
1152 ];
1153 $sourceContactID = $this->individualCreate($sourceContactParams);
1154 $sourceDisplayName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $sourceContactID, 'display_name');
1155
1156 // create an activity using API
1157 $params = [
1158 'source_contact_id' => $sourceContactID,
1159 'subject' => 'Scheduling Meeting',
1160 'activity_type_id' => 'Meeting',
1161 'assignee_contact_id' => [$assigneeContactId],
1162 'activity_date_time' => 'now',
1163 ];
1164 $activity = $this->callAPISuccess('Activity', 'create', $params);
1165
1166 // Check that from address is in "Source-Display-Name <source-email>"
1167 $formAddress = CRM_Case_BAO_Case::getReceiptFrom($activity['id']);
1168 $expectedFromAddress = sprintf('%s <%s>', $sourceDisplayName, $sourceContactParams['email']);
1169 $this->assertEquals($expectedFromAddress, $formAddress);
1170
1171 // Case 2: System Default From Address
1172 // but first erase the email address of existing source contact ID
1173 $withoutEmailParams = [
1174 'email' => '',
1175 ];
1176 $sourceContactID = $this->individualCreate($withoutEmailParams);
1177 $params = [
1178 'source_contact_id' => $sourceContactID,
1179 'subject' => 'Scheduling Meeting 2',
1180 'activity_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Meeting'),
1181 'activity_date_time' => date('Ymd'),
1182 ];
1183 $activity = $this->callAPISuccess('Activity', 'create', $params);
1184 // fetch domain info
1185 $domainInfo = $this->callAPISuccess('Domain', 'getsingle', ['id' => CRM_Core_Config::domainID()]);
1186
1187 $formAddress = CRM_Case_BAO_Case::getReceiptFrom($activity['id']);
1188 if (!empty($domainInfo['from_email'])) {
1189 $expectedFromAddress = sprintf('%s <%s>', $domainInfo['from_name'], $domainInfo['from_email']);
1190 }
1191 // Case 3: fetch default Organization Contact email address
1192 elseif (!empty($domainInfo['domain_email'])) {
1193 $expectedFromAddress = sprintf('%s <%s>', $domainInfo['name'], $domainInfo['domain_email']);
1194 }
1195 $this->assertEquals($expectedFromAddress, $formAddress);
1196
1197 // TODO: Case 4 about checking the $formAddress on basis of logged contact ID respectively needs,
1198 // to change the domain setting, which isn't straight forward in test environment
1199 }
1200
1201 /**
1202 * Set up for testing activity queries.
1203 */
1204 protected function setUpForActivityDashboardTests() {
1205 $this->createTestActivities();
1206
1207 $this->_params = [
1208 'contact_id' => NULL,
1209 'admin' => TRUE,
1210 'caseId' => NULL,
1211 'context' => 'home',
1212 'activity_type_id' => NULL,
1213 // for dashlet the Scheduled status is set by default
1214 'activity_status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Scheduled'),
1215 'offset' => 0,
1216 'rowCount' => 0,
1217 'sort' => NULL,
1218 ];
1219 }
1220
1221 /**
1222 * @throws \CRM_Core_Exception
1223 * @throws \CiviCRM_API3_Exception
1224 */
1225 public function testSendEmailBasic(): void {
1226 $contactId = $this->individualCreate();
1227
1228 // create a logged in USER since the code references it for sendEmail user.
1229 $loggedInUser = $this->createLoggedInUser();
1230
1231 $contactDetailsIntersectKeys = [
1232 'contact_id' => '',
1233 'sort_name' => '',
1234 'display_name' => '',
1235 'do_not_email' => '',
1236 'preferred_mail_format' => '',
1237 'is_deceased' => '',
1238 'email' => '',
1239 'on_hold' => '',
1240 ];
1241
1242 $contact = $this->callAPISuccess('Contact', 'getsingle', ['id' => $contactId, 'return' => array_keys($contactDetailsIntersectKeys)]);
1243 $contactDetailsIntersectKeys = [
1244 'contact_id' => '',
1245 'sort_name' => '',
1246 'display_name' => '',
1247 'do_not_email' => '',
1248 'preferred_mail_format' => '',
1249 'is_deceased' => '',
1250 'email' => '',
1251 'on_hold' => '',
1252 ];
1253 $contactDetails = [
1254 array_intersect_key($contact, $contactDetailsIntersectKeys),
1255 ];
1256
1257 $subject = __FUNCTION__ . ' subject';
1258 $html = __FUNCTION__ . ' html';
1259 $text = __FUNCTION__ . ' text';
1260 $userID = $loggedInUser;
1261
1262 [$sent, $activity_ids] = CRM_Activity_BAO_Activity::sendEmail(
1263 $contactDetails,
1264 $subject,
1265 $text,
1266 $html,
1267 $contact['email'],
1268 $userID,
1269 $from = __FUNCTION__ . '@example.com'
1270 );
1271
1272 $activity = $this->callAPISuccessGetSingle('Activity', ['id' => $activity_ids[0], 'return' => ['details', 'subject']]);
1273 $details = "-ALTERNATIVE ITEM 0-
1274 $html
1275 -ALTERNATIVE ITEM 1-
1276 $text
1277 -ALTERNATIVE END-
1278 ";
1279 $this->assertEquals($activity['details'], $details, 'Activity details does not match.');
1280 $this->assertEquals($activity['subject'], $subject, 'Activity subject does not match.');
1281 }
1282
1283 public function testSendEmailWithCampaign() {
1284 // Create a contact and contactDetails array.
1285 $contactId = $this->individualCreate();
1286
1287 // create a logged in USER since the code references it for sendEmail user.
1288 $this->createLoggedInUser();
1289 $session = CRM_Core_Session::singleton();
1290 $loggedInUser = $session->get('userID');
1291
1292 $contact = $this->civicrm_api('contact', 'getsingle', ['id' => $contactId, 'version' => $this->_apiversion]);
1293 $contactDetailsIntersectKeys = [
1294 'contact_id' => '',
1295 'sort_name' => '',
1296 'display_name' => '',
1297 'do_not_email' => '',
1298 'preferred_mail_format' => '',
1299 'is_deceased' => '',
1300 'email' => '',
1301 'on_hold' => '',
1302 ];
1303 $contactDetails = [
1304 array_intersect_key($contact, $contactDetailsIntersectKeys),
1305 ];
1306
1307 // Create a campaign.
1308 $result = $this->civicrm_api('Campaign', 'create', [
1309 'version' => $this->_apiversion,
1310 'title' => __FUNCTION__ . ' campaign',
1311 ]);
1312 $campaign_id = $result['id'];
1313
1314 $subject = __FUNCTION__ . ' subject';
1315 $html = __FUNCTION__ . ' html';
1316 $text = __FUNCTION__ . ' text';
1317 $userID = $loggedInUser;
1318
1319 [$sent, $activity_ids] = $email_result = CRM_Activity_BAO_Activity::sendEmail(
1320 $contactDetails,
1321 $subject,
1322 $text,
1323 $html,
1324 $contact['email'],
1325 $userID,
1326 $from = __FUNCTION__ . '@example.com',
1327 $attachments = NULL,
1328 $cc = NULL,
1329 $bcc = NULL,
1330 $contactIds = NULL,
1331 $additionalDetails = NULL,
1332 NULL,
1333 $campaign_id
1334 );
1335 $activity = $this->civicrm_api('activity', 'getsingle', ['id' => $activity_ids[0], 'version' => $this->_apiversion]);
1336 $this->assertEquals($activity['campaign_id'], $campaign_id, 'Activity campaign_id does not match.');
1337 }
1338
1339 /**
1340 */
1341 public function testSendSMSWithoutPermission() {
1342 $dummy = NULL;
1343 $session = CRM_Core_Session::singleton();
1344 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
1345 $this->expectException(CRM_Core_Exception::class);
1346 $this->expectExceptionMessage('You do not have the \'send SMS\' permission');
1347 CRM_Activity_BAO_Activity::sendSMS(
1348 $dummy,
1349 $dummy,
1350 $dummy,
1351 $dummy,
1352 $session->get('userID')
1353 );
1354 }
1355
1356 /**
1357 * Test that a sms does not send when a phone number is not available.
1358 *
1359 * @throws \API_Exception
1360 * @throws \CRM_Core_Exception
1361 * @throws \CiviCRM_API3_Exception
1362 * @throws \Civi\API\Exception\UnauthorizedException
1363 */
1364 public function testSendSmsNoPhoneNumber(): void {
1365 $sent = $this->createSendSmsTest(FALSE);
1366 $this->assertEquals('Recipient phone number is invalid or recipient does not want to receive SMS', $sent[0], "Expected error doesn't match");
1367 }
1368
1369 /**
1370 * @throws \API_Exception
1371 * @throws \CRM_Core_Exception
1372 * @throws \CiviCRM_API3_Exception
1373 * @throws \Civi\API\Exception\UnauthorizedException
1374 */
1375 public function testSendSmsLandLinePhoneNumber(): void {
1376 $sent = $this->createSendSmsTest(FALSE, 1);
1377 $this->assertEquals('Recipient phone number is invalid or recipient does not want to receive SMS', $sent[0], "Expected error doesn't match");
1378 }
1379
1380 /**
1381 * @throws \API_Exception
1382 * @throws \CRM_Core_Exception
1383 * @throws \CiviCRM_API3_Exception
1384 * @throws \Civi\API\Exception\UnauthorizedException
1385 */
1386 public function testSendSmsMobilePhoneNumber(): void {
1387 $sent = $this->createSendSmsTest(TRUE, 2);
1388 $this->assertEquals(TRUE, $sent[0], "Expected sent should be true");
1389 }
1390
1391 /**
1392 * Test that when a number is specified in the To Param of the SMS provider parameters that an SMS is sent
1393 * @see dev/core/#273
1394 */
1395 public function testSendSMSMobileInToProviderParam(): void {
1396 $sent = $this->createSendSmsTest(TRUE, 2, TRUE);
1397 $this->assertEquals(TRUE, $sent[0], 'Expected sent should be true');
1398 }
1399
1400 /**
1401 * Test that when a numbe ris specified in the To Param of the SMS provider parameters that an SMS is sent
1402 * @see dev/core/#273
1403 */
1404 public function testSendSMSMobileInToProviderParamWithDoNotSMS(): void {
1405 $sent = $this->createSendSmsTest(FALSE, 2, TRUE, ['do_not_sms' => 1]);
1406 foreach ($sent as $error) {
1407 $this->assertEquals('Contact Does not accept SMS', $error);
1408 }
1409 $this->assertCount(1, $sent, 'Expected sent should a PEAR Error');
1410 }
1411
1412 /**
1413 * @param bool $expectSuccess
1414 * @param int $phoneType (0=no phone, phone_type option group (1=fixed,
1415 * 2=mobile)
1416 * @param bool $passPhoneTypeInContactDetails
1417 * @param array $additionalContactParams additional contact creation params
1418 *
1419 * @return array
1420 * @throws \API_Exception
1421 * @throws \CRM_Core_Exception
1422 * @throws \CiviCRM_API3_Exception
1423 * @throws \Civi\API\Exception\UnauthorizedException
1424 */
1425 public function createSendSmsTest(bool $expectSuccess = TRUE, int $phoneType = 0, bool $passPhoneTypeInContactDetails = FALSE, array $additionalContactParams = []): array {
1426 $provider = civicrm_api3('SmsProvider', 'create', [
1427 'name' => 'CiviTestSMSProvider',
1428 'api_type' => 1,
1429 'username' => 1,
1430 'password' => 1,
1431 'api_url' => 1,
1432 'api_params' => 'a=1',
1433 'is_default' => 1,
1434 'is_active' => 1,
1435 'domain_id' => 1,
1436 ]);
1437
1438 $smsProviderParams['provider_id'] = $provider['id'];
1439
1440 // Create a contact
1441 $contactId = $this->individualCreate();
1442 if (!empty($additionalContactParams)) {
1443 $this->callAPISuccess('contact', 'create', ['id' => $contactId] + $additionalContactParams);
1444 }
1445 $contactsResult = $this->callApiSuccess('Contact', 'get', ['id' => $contactId, 'return' => ['id', 'phone_type_id', 'do_not_sms']]);
1446 $contactDetails = $contactsResult['values'];
1447
1448 // Get contactIds from contact details
1449 foreach ($contactDetails as $contact) {
1450 $contactIds[] = $contact['contact_id'];
1451 }
1452
1453 $activityParams['sms_text_message'] = 'text';
1454 $activityParams['activity_subject'] = 'subject';
1455
1456 // Get a "logged in" user to set as source of Sms.
1457 $session = CRM_Core_Session::singleton();
1458 $sourceContactId = $session->get('userID');
1459
1460 $this->createLoggedInUser();
1461
1462 // Give user permission to 'send SMS'
1463 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM', 'send SMS'];
1464
1465 // Create a phone number
1466 switch ($phoneType) {
1467 case 0:
1468 // No phone number
1469 break;
1470
1471 case 2:
1472 // Create a mobile phone number
1473 $contactDetails = $this->createMobilePhone($contactId, $passPhoneTypeInContactDetails, $contactDetails);
1474 break;
1475
1476 case 1:
1477 // Create a fixed phone number
1478 $phone = civicrm_api3('Phone', 'create', [
1479 'contact_id' => $contactId,
1480 'phone' => 654321,
1481 'phone_type_id' => 'Phone',
1482 ]);
1483 if ($passPhoneTypeInContactDetails) {
1484 $contactDetails[$contactId]['phone'] = $phone['values'][$phone['id']]['phone'];
1485 $contactDetails[$contactId]['phone_type_id'] = $phone['values'][$phone['id']]['phone_type_id'];
1486 }
1487 break;
1488 }
1489
1490 // Now run the actual test
1491 [$sent, $activityId, $success] = CRM_Activity_BAO_Activity::sendSms(
1492 $contactDetails,
1493 $activityParams,
1494 $smsProviderParams,
1495 $contactIds,
1496 $sourceContactId
1497 );
1498 $this->validateActivity($activityId);
1499 $this->assertEquals($expectSuccess, $success);
1500 return (array) $sent;
1501 }
1502
1503 /**
1504 * @throws \CRM_Core_Exception
1505 */
1506 protected function createTestActivities(): void {
1507 $this->loadXMLDataSet(__DIR__ . '/activities_for_dashboard_count.xml');
1508 // Make changes to improve variation in php since the xml method is brittle & relies on option values being unchanged.
1509 $this->callAPISuccess('Activity', 'create', ['id' => 12, 'activity_type_id' => 'Bulk Email']);
1510 }
1511
1512 /**
1513 * ACL HOOK implementation for various tests
1514 */
1515 public function hook_civicrm_aclWhereClause($type, &$tables, &$whereTables, &$contactID, &$where): void {
1516 if (!empty($this->allowedContactsACL)) {
1517 $contact_id_list = implode(',', $this->allowedContactsACL);
1518 $where = " contact_a.id IN ($contact_id_list)";
1519 }
1520 }
1521
1522 public function testCaseTokens() {
1523 $caseTest = new CiviCaseTestCase();
1524 $caseTest->setUp();
1525 // Create a contact and contactDetails array.
1526 $contactId = $this->individualCreate();
1527
1528 // create a case for this user
1529 $result = $this->callAPISuccess('Case', 'create', [
1530 'contact_id' => $contactId,
1531 'case_type_id' => '1',
1532 'subject' => "my case",
1533 'status_id' => "Open",
1534 ]);
1535
1536 $caseId = $result['id'];
1537 $html_message = "<p>This is a test case with id: {case.id} and subject: {case.subject}</p>";
1538 $html_message = CRM_Utils_Token::replaceCaseTokens($caseId, $html_message);
1539
1540 $this->assertTrue(strpos($html_message, 'id: ' . $caseId) !== 0);
1541 $this->assertTrue(strpos($html_message, 'subject: my case') !== 0);
1542 $caseTest->tearDown();
1543 }
1544
1545 public function testSendEmailWithCaseId() {
1546 $caseTest = new CiviCaseTestCase();
1547 $caseTest->setUp();
1548 // Create a contact and contactDetails array.
1549 $contactId = $this->individualCreate();
1550 $contact = $this->callAPISuccess('Contact', 'get', ['id' => $contactId]);
1551
1552 // create a logged in USER since the code references it for sendEmail user.
1553 $this->createLoggedInUser();
1554 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['view all contacts', 'access CiviCRM', 'access all cases and activities', 'administer CiviCase'];
1555 $session = CRM_Core_Session::singleton();
1556 $loggedInUser = $session->get('userID');
1557
1558 // create a case for this user
1559 $result = $this->callAPISuccess('Case', 'create', [
1560 'contact_id' => $contactId,
1561 'case_type_id' => 1,
1562 'subject' => "my case",
1563 'status_id' => "Open",
1564 ]);
1565
1566 $caseId = $result['id'];
1567
1568 $subject = __FUNCTION__ . ' subject {case.subject}';
1569 $html = __FUNCTION__ . ' html {case.subject}';
1570 $text = __FUNCTION__ . ' text';
1571
1572 $mut = new CiviMailUtils($this, TRUE);
1573 [$sent, $activity_ids] = $email_result = CRM_Activity_BAO_Activity::sendEmail(
1574 $contact['values'],
1575 $subject,
1576 $text,
1577 $html,
1578 $contact['values'][$contactId]['email'],
1579 $loggedInUser,
1580 $from = __FUNCTION__ . '@example.com',
1581 NULL,
1582 NULL,
1583 NULL,
1584 [$contactId],
1585 NULL,
1586 NULL,
1587 NULL,
1588 $caseId
1589 );
1590 $activity = $this->callAPISuccess('Activity', 'getsingle', ['id' => $activity_ids[0], 'return' => ['case_id']]);
1591 $this->assertEquals($caseId, $activity['case_id'][0], 'Activity case_id does not match.');
1592 $mut->checkMailLog(['subject my case']);
1593 $mut->stop();
1594 }
1595
1596 /**
1597 * Checks that tokens are uniquely replaced for contacts.
1598 */
1599 public function testSendEmailWillReplaceTokensUniquelyForEachContact() {
1600 $contactId1 = $this->individualCreate(['last_name' => 'Red']);
1601 $contactId2 = $this->individualCreate(['last_name' => 'Pink']);
1602
1603 // create a logged in USER since the code references it for sendEmail user.
1604 $this->createLoggedInUser();
1605 $session = CRM_Core_Session::singleton();
1606 $loggedInUser = $session->get('userID');
1607 $contact = $this->callAPISuccess('Contact', 'get', ['sequential' => 1, 'id' => ['IN' => [$contactId1, $contactId2]]]);
1608
1609 // Create a campaign.
1610 $result = $this->callAPISuccess('Campaign', 'create', [
1611 'version' => $this->_apiversion,
1612 'title' => __FUNCTION__ . ' campaign',
1613 ]);
1614 $campaign_id = $result['id'];
1615
1616 // Add contact tokens in subject, html , text.
1617 $subject = __FUNCTION__ . ' subject' . '{contact.display_name}';
1618 $html = __FUNCTION__ . ' html' . '{contact.display_name}';
1619 $text = __FUNCTION__ . ' text' . '{contact.display_name}';
1620 $userID = $loggedInUser;
1621
1622 CRM_Activity_BAO_Activity::sendEmail(
1623 $contact['values'],
1624 $subject,
1625 $text,
1626 $html,
1627 $contact['values'][0]['email'],
1628 $userID,
1629 $from = __FUNCTION__ . '@example.com',
1630 $attachments = NULL,
1631 $cc = NULL,
1632 $bcc = NULL,
1633 $contactIds = array_column($contact['values'], 'id'),
1634 $additionalDetails = NULL,
1635 NULL,
1636 $campaign_id
1637 );
1638 $result = $this->callAPISuccess('activity', 'get', ['campaign_id' => $campaign_id]);
1639 // An activity created for each of the two contacts
1640 $this->assertEquals(2, $result['count']);
1641 $id = 0;
1642 foreach ($result['values'] as $activity) {
1643 $htmlValue = str_replace('{contact.display_name}', $contact['values'][$id]['display_name'], $html);
1644 $textValue = str_replace('{contact.display_name}', $contact['values'][$id]['display_name'], $text);
1645 $subjectValue = str_replace('{contact.display_name}', $contact['values'][$id]['display_name'], $subject);
1646 $details = "-ALTERNATIVE ITEM 0-
1647 $htmlValue
1648 -ALTERNATIVE ITEM 1-
1649 $textValue
1650 -ALTERNATIVE END-
1651 ";
1652 $this->assertEquals($activity['details'], $details, 'Activity details does not match.');
1653 $this->assertEquals($activity['subject'], $subjectValue, 'Activity subject does not match.');
1654 $id++;
1655 }
1656 }
1657
1658 /**
1659 * Test that smarty is rendered, if enabled.
1660 *
1661 * @throws \CRM_Core_Exception
1662 * @throws \CiviCRM_API3_Exception
1663 */
1664 public function testSmartyEnabled(): void {
1665 putenv('CIVICRM_MAIL_SMARTY=1');
1666 $this->createLoggedInUser();
1667 $contactID = $this->individualCreate(['last_name' => 'Red']);
1668 CRM_Activity_BAO_Activity::sendEmail(
1669 [
1670 $contactID => [
1671 'preferred_mail_format' => 'Both',
1672 'contact_id' => $contactID,
1673 'email' => 'a@example.com',
1674 ],
1675 ],
1676 '{contact.first_name} {$contact.first_name}',
1677 '{contact.first_name} {$contact.first_name}',
1678 '{contact.first_name} {$contact.first_name}',
1679 NULL,
1680 NULL,
1681 'mail@example.com',
1682 NULL,
1683 NULL,
1684 NULL,
1685 [$contactID]
1686 );
1687 $activity = $this->callAPISuccessGetValue('Activity', ['return' => 'details']);
1688 putenv('CIVICRM_MAIL_SMARTY=0');
1689 }
1690
1691 /**
1692 * Same as testSendEmailWillReplaceTokensUniquelyForEachContact but with
1693 * 3 recipients and an attachment.
1694 *
1695 * @throws \CRM_Core_Exception
1696 * @throws \CiviCRM_API3_Exception
1697 */
1698 public function testSendEmailWillReplaceTokensUniquelyForEachContact3(): void {
1699 $contactId1 = $this->individualCreate(['last_name' => 'Red']);
1700 $contactId2 = $this->individualCreate(['last_name' => 'Pink']);
1701 $contactId3 = $this->individualCreate(['last_name' => 'Ochre']);
1702
1703 // create a logged in USER since the code references it for sendEmail user.
1704 $loggedInUser = $this->createLoggedInUser();
1705 $contact = $this->callAPISuccess('Contact', 'get', ['sequential' => 1, 'id' => ['IN' => [$contactId1, $contactId2, $contactId3]]]);
1706
1707 // Add contact tokens in subject, html , text.
1708 $subject = __FUNCTION__ . ' subject' . '{contact.display_name}';
1709 $html = __FUNCTION__ . ' html' . '{contact.display_name}';
1710 // Check the smarty doesn't mess stuff up.
1711 $text = ' text' . '{contact.display_name} {$contact.first_name}';
1712
1713 $filepath = Civi::paths()->getPath('[civicrm.files]/custom');
1714 $fileName = 'test_email_create.txt';
1715 $fileUri = "{$filepath}/{$fileName}";
1716 // Create a file.
1717 CRM_Utils_File::createFakeFile($filepath, 'aaaaaa', $fileName);
1718 $attachments = [
1719 'attachFile_1' =>
1720 [
1721 'uri' => $fileUri,
1722 'type' => 'text/plain',
1723 'location' => $fileUri,
1724 ],
1725 ];
1726
1727 CRM_Activity_BAO_Activity::sendEmail(
1728 $contact['values'],
1729 $subject,
1730 $text,
1731 $html,
1732 $contact['values'][0]['email'],
1733 $loggedInUser,
1734 __FUNCTION__ . '@example.com',
1735 $attachments,
1736 NULL,
1737 NULL,
1738 array_column($contact['values'], 'id'),
1739 NULL,
1740 NULL,
1741 $this->getCampaignID()
1742 );
1743 $result = $this->callAPISuccess('Activity', 'get', ['campaign_id' => $this->getCampaignID()]);
1744 // An activity created for each of the two contacts
1745 $this->assertEquals(3, $result['count']);
1746 $id = 0;
1747 foreach ($result['values'] as $activity) {
1748 $htmlValue = str_replace('{contact.display_name}', $contact['values'][$id]['display_name'], $html);
1749 $textValue = str_replace('{contact.display_name}', $contact['values'][$id]['display_name'], $text);
1750 $subjectValue = str_replace('{contact.display_name}', $contact['values'][$id]['display_name'], $subject);
1751 $details = "-ALTERNATIVE ITEM 0-
1752 $htmlValue
1753 -ALTERNATIVE ITEM 1-
1754 $textValue
1755 -ALTERNATIVE END-
1756 ";
1757 $this->assertEquals($activity['details'], $details, 'Activity details does not match.');
1758 $this->assertEquals($activity['subject'], $subjectValue, 'Activity subject does not match.');
1759 $id++;
1760 }
1761
1762 unlink($fileUri);
1763 }
1764
1765 /**
1766 * Checks that attachments are not duplicated for activities.
1767 */
1768 public function testSendEmailDoesNotDuplicateAttachmentFileIdsForActivitiesCreated() {
1769 $contactId1 = $this->individualCreate(['last_name' => 'Red']);
1770 $contactId2 = $this->individualCreate(['last_name' => 'Pink']);
1771
1772 // create a logged in USER since the code references it for sendEmail user.
1773 $this->createLoggedInUser();
1774 $session = CRM_Core_Session::singleton();
1775 $loggedInUser = $session->get('userID');
1776 $contact = $this->callAPISuccess('Contact', 'get', ['sequential' => 1, 'id' => ['IN' => [$contactId1, $contactId2]]]);
1777
1778 // Create a campaign.
1779 $result = $this->callAPISuccess('Campaign', 'create', [
1780 'version' => $this->_apiversion,
1781 'title' => __FUNCTION__ . ' campaign',
1782 ]);
1783 $campaign_id = $result['id'];
1784
1785 $subject = __FUNCTION__ . ' subject';
1786 $html = __FUNCTION__ . ' html';
1787 $text = __FUNCTION__ . ' text';
1788 $userID = $loggedInUser;
1789
1790 $filepath = Civi::paths()->getPath('[civicrm.files]/custom');
1791 $fileName = "test_email_create.txt";
1792 $fileUri = "{$filepath}/{$fileName}";
1793 // Create a file.
1794 CRM_Utils_File::createFakeFile($filepath, 'Bananas do not bend themselves without a little help.', $fileName);
1795 $attachments = [
1796 'attachFile_1' =>
1797 [
1798 'uri' => $fileUri,
1799 'type' => 'text/plain',
1800 'location' => $fileUri,
1801 ],
1802 ];
1803
1804 CRM_Activity_BAO_Activity::sendEmail(
1805 $contact['values'],
1806 $subject,
1807 $text,
1808 $html,
1809 $contact['values'][0]['email'],
1810 $userID,
1811 $from = __FUNCTION__ . '@example.com',
1812 $attachments,
1813 $cc = NULL,
1814 $bcc = NULL,
1815 $contactIds = array_column($contact['values'], 'id'),
1816 $additionalDetails = NULL,
1817 NULL,
1818 $campaign_id
1819 );
1820 $result = $this->callAPISuccess('activity', 'get', ['campaign_id' => $campaign_id]);
1821 // An activity created for each of the two contacts, i.e two activities.
1822 $this->assertEquals(2, $result['count']);
1823 $activityIds = array_column($result['values'], 'id');
1824 $result = $this->callAPISuccess('Activity', 'get', [
1825 'return' => ['file_id'],
1826 'id' => ['IN' => $activityIds],
1827 'sequential' => 1,
1828 ]);
1829
1830 // Verify that the that both activities are linked to the same File Id.
1831 $this->assertEquals($result['values'][0]['file_id'], $result['values'][1]['file_id']);
1832 }
1833
1834 /**
1835 * Adds a case with one activity.
1836 *
1837 */
1838 protected function addCaseWithActivity() {
1839 // case is not enabled by default do that now.
1840 $enableResult = CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase');
1841 $this->assertTrue($enableResult, 'Cannot enable CiviCase in line ' . __LINE__);
1842
1843 // We need a minimal case setup.
1844 $case_type_id = civicrm_api3('CaseType', 'get', ['return' => 'id', 'name' => 'test_case_type'])['id'] ?? NULL;
1845 if (!$case_type_id) {
1846 $params = [
1847 'name' => 'test_case_type',
1848 'title' => 'test_case_type',
1849 "is_active" => "1",
1850 "definition" => [
1851 "activityTypes" => [
1852 ["name" => "Open Case", "max_instances" => "1"],
1853 ["name" => "Meeting"],
1854 ],
1855 "activitySets" => [
1856 [
1857 "name" => "standard_timeline",
1858 "label" => "Standard Timeline",
1859 "timeline" => "1",
1860 "activityTypes" => [
1861 [
1862 "name" => "Open Case",
1863 "status" => "Completed",
1864 "label" => "Open Case",
1865 "default_assignee_type" => "1",
1866 ],
1867 ],
1868 ],
1869 ],
1870 "timelineActivityTypes" => [
1871 [
1872 "name" => 'Open Case',
1873 "status" => 'Completed',
1874 "label" => 'Open Case',
1875 "default_assignee_type" => "1",
1876 ],
1877 ],
1878 "caseRoles" => [
1879 [
1880 "name" => "Case Coordinator",
1881 "creator" => 1,
1882 "manager" => 1,
1883 ],
1884 ],
1885 ],
1886 ];
1887 $case_type_id = $this->callAPISuccess('CaseType', 'create', $params)['id'];
1888 }
1889
1890 // Create a case with Contact #3 as the client.
1891 $case_id = civicrm_api3('case', 'get', ['subject' => 'test case 1'])['id'] ?? NULL;
1892 if (!$case_id) {
1893 // Create case
1894 $params = [
1895 'subject' => 'test case 1',
1896 'contact_id' => 3,
1897 'status_id' => 'Open',
1898 'case_type_id' => $case_type_id,
1899 'creator_id' => 3,
1900 ];
1901 $case_id = $this->callAPISuccess('case', 'create', $params)['id'];
1902 }
1903
1904 // Create a scheduled 'Meeting' activity that belongs to this case, but is
1905 // assigned to contact #9
1906 $activity_id = $this->callAPISuccess('Activity', 'create', [
1907 'activity_type_id' => 'Meeting',
1908 'status_id' => 'Scheduled',
1909 'case_id' => $case_id,
1910 'source_contact_id' => 3,
1911 'assignee_id' => [9],
1912 'subject' => 'test meeting activity',
1913 ])['id'] ?? NULL;
1914 }
1915
1916 /**
1917 * Change setting, and the cache of it.
1918 */
1919 protected function setShowCaseActivitiesInCore(bool $val) {
1920 Civi::settings()->set('civicaseShowCaseActivities', $val ? 1 : 0);
1921 CRM_Core_Component::getEnabledComponents();
1922 Civi::$statics['CRM_Core_Component']['info']['CiviCase'] = new CRM_Case_Info('CiviCase', 'CRM_Case', 7);
1923 Civi::$statics['CRM_Core_Component']['info']['CiviCase']->info['showActivitiesInCore'] = $val;
1924 }
1925
1926 /**
1927 * Test multiple variations of target and assignee contacts in create
1928 * and edit mode.
1929 *
1930 * @dataProvider targetAndAssigneeProvider
1931 *
1932 * @param array $do_first
1933 * @param array $do_second
1934 *
1935 * @throws \CiviCRM_API3_Exception
1936 */
1937 public function testTargetAssigneeVariations(array $do_first, array $do_second) {
1938 // Originally wanted to put this in setUp() but it broke other tests.
1939 $this->loggedInUserId = $this->createLoggedInUser();
1940 for ($i = 1; $i <= 4; $i++) {
1941 $this->someContacts[$i] = $this->individualCreate([], $i - 1, TRUE);
1942 }
1943
1944 $params = [
1945 'activity_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Meeting'),
1946 'subject' => 'Test Meeting',
1947 'source_contact_id' => $this->loggedInUserId,
1948 ];
1949
1950 // Create an activity first if specified.
1951 $activity = NULL;
1952 if (!empty($do_first)) {
1953 if (!empty($do_first['targets'])) {
1954 // e.g. if it is [1], then pick $someContacts[1]. If it's [1,2], then
1955 // pick $someContacts[1] and $someContacts[2].
1956 $params['target_contact_id'] = array_values(array_intersect_key($this->someContacts, array_flip($do_first['targets'])));
1957 }
1958 if (!empty($do_first['assignees'])) {
1959 $params['assignee_contact_id'] = array_values(array_intersect_key($this->someContacts, array_flip($do_first['assignees'])));
1960 }
1961
1962 $activity = CRM_Activity_BAO_Activity::create($params);
1963 $this->assertNotEmpty($activity->id);
1964
1965 $params['id'] = $activity->id;
1966 }
1967
1968 // Now do the second one, which will either create or update depending what
1969 // we did first.
1970 $params['target_contact_id'] = array_values(array_intersect_key($this->someContacts, array_flip($do_second['targets'])));
1971 $params['assignee_contact_id'] = array_values(array_intersect_key($this->someContacts, array_flip($do_second['assignees'])));
1972 $activity = CRM_Activity_BAO_Activity::create($params);
1973
1974 // Check targets
1975 $queryParams = [
1976 1 => [$activity->id, 'Integer'],
1977 2 => [CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Targets'), 'Integer'],
1978 ];
1979 $this->assertEquals($params['target_contact_id'], array_column(CRM_Core_DAO::executeQuery('SELECT contact_id FROM civicrm_activity_contact WHERE activity_id = %1 AND record_type_id = %2', $queryParams)->fetchAll(), 'contact_id'));
1980
1981 // Check assignees
1982 $queryParams = [
1983 1 => [$activity->id, 'Integer'],
1984 2 => [CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Assignees'), 'Integer'],
1985 ];
1986 $this->assertEquals($params['assignee_contact_id'], array_column(CRM_Core_DAO::executeQuery('SELECT contact_id FROM civicrm_activity_contact WHERE activity_id = %1 AND record_type_id = %2', $queryParams)->fetchAll(), 'contact_id'));
1987
1988 // Clean up
1989 foreach ($this->someContacts as $cid) {
1990 $this->callAPISuccess('Contact', 'delete', ['id' => $cid]);
1991 }
1992 }
1993
1994 /**
1995 * Same as testTargetAssigneeVariations but passes the target/assignee
1996 * in as a scalar when there's only one of them.
1997 *
1998 * @dataProvider targetAndAssigneeProvider
1999 *
2000 * @param array $do_first
2001 * @param array $do_second
2002 *
2003 * @throws \CRM_Core_Exception
2004 * @throws \CiviCRM_API3_Exception
2005 */
2006 public function testTargetAssigneeVariationsWithScalars(array $do_first, array $do_second) {
2007 // Originally wanted to put this in setUp() but it broke other tests.
2008 $this->loggedInUserId = $this->createLoggedInUser();
2009 for ($i = 1; $i <= 4; $i++) {
2010 $this->someContacts[$i] = $this->individualCreate([], $i - 1, TRUE);
2011 }
2012
2013 $params = [
2014 'activity_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Meeting'),
2015 'subject' => 'Test Meeting',
2016 'source_contact_id' => $this->loggedInUserId,
2017 ];
2018
2019 // Create an activity first if specified.
2020 $activity = NULL;
2021 if (!empty($do_first)) {
2022 if (!empty($do_first['targets'])) {
2023 // e.g. if it is [1], then pick $someContacts[1]. If it's [1,2], then
2024 // pick $someContacts[1] and $someContacts[2].
2025 $params['target_contact_id'] = array_values(array_intersect_key($this->someContacts, array_flip($do_first['targets'])));
2026 if (count($params['target_contact_id']) == 1) {
2027 $params['target_contact_id'] = $params['target_contact_id'][0];
2028 }
2029 }
2030 if (!empty($do_first['assignees'])) {
2031 $params['assignee_contact_id'] = array_values(array_intersect_key($this->someContacts, array_flip($do_first['assignees'])));
2032 if (count($params['assignee_contact_id']) == 1) {
2033 $params['assignee_contact_id'] = $params['assignee_contact_id'][0];
2034 }
2035 }
2036
2037 $activity = CRM_Activity_BAO_Activity::create($params);
2038 $this->assertNotEmpty($activity->id);
2039
2040 $params['id'] = $activity->id;
2041 }
2042
2043 // Now do the second one, which will either create or update depending what
2044 // we did first.
2045 $params['target_contact_id'] = array_values(array_intersect_key($this->someContacts, array_flip($do_second['targets'])));
2046 if (count($params['target_contact_id']) == 1) {
2047 $params['target_contact_id'] = $params['target_contact_id'][0];
2048 }
2049 $params['assignee_contact_id'] = array_values(array_intersect_key($this->someContacts, array_flip($do_second['assignees'])));
2050 if (count($params['assignee_contact_id']) == 1) {
2051 $params['assignee_contact_id'] = $params['assignee_contact_id'][0];
2052 }
2053 $activity = CRM_Activity_BAO_Activity::create($params);
2054
2055 // Check targets
2056 $queryParams = [
2057 1 => [$activity->id, 'Integer'],
2058 2 => [CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Targets'), 'Integer'],
2059 ];
2060 $this->assertEquals((array) $params['target_contact_id'], array_column(CRM_Core_DAO::executeQuery('SELECT contact_id FROM civicrm_activity_contact WHERE activity_id = %1 AND record_type_id = %2', $queryParams)->fetchAll(), 'contact_id'));
2061
2062 // Check assignees
2063 $queryParams = [
2064 1 => [$activity->id, 'Integer'],
2065 2 => [CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Assignees'), 'Integer'],
2066 ];
2067 $this->assertEquals((array) $params['assignee_contact_id'], array_column(CRM_Core_DAO::executeQuery('SELECT contact_id FROM civicrm_activity_contact WHERE activity_id = %1 AND record_type_id = %2', $queryParams)->fetchAll(), 'contact_id'));
2068
2069 // Clean up
2070 foreach ($this->someContacts as $cid) {
2071 $this->callAPISuccess('Contact', 'delete', ['id' => $cid]);
2072 }
2073 }
2074
2075 /**
2076 * Dataprovider for testTargetAssigneeVariations
2077 * @return array
2078 */
2079 public function targetAndAssigneeProvider():array {
2080 return [
2081 // Explicit index so that it's easy to see which one has failed without
2082 // having to finger count.
2083 0 => [
2084 'do first' => [
2085 // Completely empty array means don't create any activity first,
2086 // as opposed to the ones we do later where "do first" has member
2087 // elements but those are empty, which means create an activity first
2088 // but with no contacts.
2089 ],
2090 'do second' => [
2091 'targets' => [],
2092 'assignees' => [],
2093 ],
2094 ],
2095 1 => [
2096 'do first' => [],
2097 'do second' => [
2098 'targets' => [1],
2099 'assignees' => [],
2100 ],
2101 ],
2102 2 => [
2103 'do first' => [],
2104 'do second' => [
2105 'targets' => [1, 2],
2106 'assignees' => [],
2107 ],
2108 ],
2109 3 => [
2110 'do first' => [],
2111 'do second' => [
2112 'targets' => [],
2113 'assignees' => [3],
2114 ],
2115 ],
2116 4 => [
2117 'do first' => [],
2118 'do second' => [
2119 'targets' => [],
2120 'assignees' => [3, 4],
2121 ],
2122 ],
2123 5 => [
2124 'do first' => [],
2125 'do second' => [
2126 'targets' => [1],
2127 'assignees' => [3],
2128 ],
2129 ],
2130 6 => [
2131 'do first' => [],
2132 'do second' => [
2133 'targets' => [1, 2],
2134 'assignees' => [3],
2135 ],
2136 ],
2137 7 => [
2138 'do first' => [],
2139 'do second' => [
2140 'targets' => [1, 2],
2141 'assignees' => [3, 4],
2142 ],
2143 ],
2144 // The next sets test the same thing again but updating an activity
2145 // that has no contacts
2146 8 => [
2147 'do first' => [
2148 'targets' => [],
2149 'assignees' => [],
2150 ],
2151 'do second' => [
2152 'targets' => [],
2153 'assignees' => [],
2154 ],
2155 ],
2156 9 => [
2157 'do first' => [
2158 'targets' => [],
2159 'assignees' => [],
2160 ],
2161 'do second' => [
2162 'targets' => [1],
2163 'assignees' => [],
2164 ],
2165 ],
2166 10 => [
2167 'do first' => [
2168 'targets' => [],
2169 'assignees' => [],
2170 ],
2171 'do second' => [
2172 'targets' => [1, 2],
2173 'assignees' => [],
2174 ],
2175 ],
2176 11 => [
2177 'do first' => [
2178 'targets' => [],
2179 'assignees' => [],
2180 ],
2181 'do second' => [
2182 'targets' => [],
2183 'assignees' => [3],
2184 ],
2185 ],
2186 12 => [
2187 'do first' => [
2188 'targets' => [],
2189 'assignees' => [],
2190 ],
2191 'do second' => [
2192 'targets' => [],
2193 'assignees' => [3, 4],
2194 ],
2195 ],
2196 13 => [
2197 'do first' => [
2198 'targets' => [],
2199 'assignees' => [],
2200 ],
2201 'do second' => [
2202 'targets' => [1],
2203 'assignees' => [3],
2204 ],
2205 ],
2206 14 => [
2207 'do first' => [
2208 'targets' => [],
2209 'assignees' => [],
2210 ],
2211 'do second' => [
2212 'targets' => [1, 2],
2213 'assignees' => [3],
2214 ],
2215 ],
2216 15 => [
2217 'do first' => [
2218 'targets' => [],
2219 'assignees' => [],
2220 ],
2221 'do second' => [
2222 'targets' => [1, 2],
2223 'assignees' => [3, 4],
2224 ],
2225 ],
2226 // And again but updating an activity with 1 contact
2227 16 => [
2228 'do first' => [
2229 'targets' => [1],
2230 'assignees' => [],
2231 ],
2232 'do second' => [
2233 'targets' => [],
2234 'assignees' => [],
2235 ],
2236 ],
2237 17 => [
2238 'do first' => [
2239 'targets' => [1],
2240 'assignees' => [],
2241 ],
2242 'do second' => [
2243 'targets' => [1],
2244 'assignees' => [],
2245 ],
2246 ],
2247 18 => [
2248 'do first' => [
2249 'targets' => [1],
2250 'assignees' => [],
2251 ],
2252 'do second' => [
2253 'targets' => [1, 2],
2254 'assignees' => [],
2255 ],
2256 ],
2257 19 => [
2258 'do first' => [
2259 'targets' => [1],
2260 'assignees' => [],
2261 ],
2262 'do second' => [
2263 'targets' => [],
2264 'assignees' => [3],
2265 ],
2266 ],
2267 20 => [
2268 'do first' => [
2269 'targets' => [1],
2270 'assignees' => [],
2271 ],
2272 'do second' => [
2273 'targets' => [],
2274 'assignees' => [3, 4],
2275 ],
2276 ],
2277 21 => [
2278 'do first' => [
2279 'targets' => [1],
2280 'assignees' => [],
2281 ],
2282 'do second' => [
2283 'targets' => [1],
2284 'assignees' => [3],
2285 ],
2286 ],
2287 22 => [
2288 'do first' => [
2289 'targets' => [1],
2290 'assignees' => [],
2291 ],
2292 'do second' => [
2293 'targets' => [1, 2],
2294 'assignees' => [3],
2295 ],
2296 ],
2297 23 => [
2298 'do first' => [
2299 'targets' => [1],
2300 'assignees' => [],
2301 ],
2302 'do second' => [
2303 'targets' => [1, 2],
2304 'assignees' => [3, 4],
2305 ],
2306 ],
2307 24 => [
2308 'do first' => [
2309 'targets' => [1],
2310 'assignees' => [],
2311 ],
2312 'do second' => [
2313 // a little different variation where we're changing the target as
2314 // opposed to adding one or deleting
2315 'targets' => [2],
2316 'assignees' => [],
2317 ],
2318 ],
2319 // And again but updating an activity with 2 contacts
2320 25 => [
2321 'do first' => [
2322 'targets' => [1, 2],
2323 'assignees' => [],
2324 ],
2325 'do second' => [
2326 'targets' => [],
2327 'assignees' => [],
2328 ],
2329 ],
2330 26 => [
2331 'do first' => [
2332 'targets' => [1, 2],
2333 'assignees' => [],
2334 ],
2335 'do second' => [
2336 'targets' => [1],
2337 'assignees' => [],
2338 ],
2339 ],
2340 27 => [
2341 'do first' => [
2342 'targets' => [1, 2],
2343 'assignees' => [],
2344 ],
2345 'do second' => [
2346 'targets' => [1, 2],
2347 'assignees' => [],
2348 ],
2349 ],
2350 28 => [
2351 'do first' => [
2352 'targets' => [1, 2],
2353 'assignees' => [],
2354 ],
2355 'do second' => [
2356 'targets' => [],
2357 'assignees' => [3],
2358 ],
2359 ],
2360 29 => [
2361 'do first' => [
2362 'targets' => [1, 2],
2363 'assignees' => [],
2364 ],
2365 'do second' => [
2366 'targets' => [],
2367 'assignees' => [3, 4],
2368 ],
2369 ],
2370 30 => [
2371 'do first' => [
2372 'targets' => [1, 2],
2373 'assignees' => [],
2374 ],
2375 'do second' => [
2376 'targets' => [1],
2377 'assignees' => [3],
2378 ],
2379 ],
2380 31 => [
2381 'do first' => [
2382 'targets' => [1, 2],
2383 'assignees' => [],
2384 ],
2385 'do second' => [
2386 'targets' => [1, 2],
2387 'assignees' => [3],
2388 ],
2389 ],
2390 32 => [
2391 'do first' => [
2392 'targets' => [1, 2],
2393 'assignees' => [],
2394 ],
2395 'do second' => [
2396 'targets' => [1, 2],
2397 'assignees' => [3, 4],
2398 ],
2399 ],
2400 33 => [
2401 'do first' => [
2402 'targets' => [1, 2],
2403 'assignees' => [],
2404 ],
2405 'do second' => [
2406 'targets' => [2],
2407 'assignees' => [],
2408 ],
2409 ],
2410 // And again but now start with assignees
2411 34 => [
2412 'do first' => [
2413 'targets' => [],
2414 'assignees' => [3],
2415 ],
2416 'do second' => [
2417 'targets' => [],
2418 'assignees' => [],
2419 ],
2420 ],
2421 35 => [
2422 'do first' => [
2423 'targets' => [],
2424 'assignees' => [3],
2425 ],
2426 'do second' => [
2427 'targets' => [1],
2428 'assignees' => [],
2429 ],
2430 ],
2431 36 => [
2432 'do first' => [
2433 'targets' => [],
2434 'assignees' => [3],
2435 ],
2436 'do second' => [
2437 'targets' => [1, 2],
2438 'assignees' => [],
2439 ],
2440 ],
2441 37 => [
2442 'do first' => [
2443 'targets' => [],
2444 'assignees' => [3],
2445 ],
2446 'do second' => [
2447 'targets' => [],
2448 'assignees' => [3],
2449 ],
2450 ],
2451 38 => [
2452 'do first' => [
2453 'targets' => [],
2454 'assignees' => [3],
2455 ],
2456 'do second' => [
2457 'targets' => [],
2458 'assignees' => [3, 4],
2459 ],
2460 ],
2461 39 => [
2462 'do first' => [
2463 'targets' => [],
2464 'assignees' => [3],
2465 ],
2466 'do second' => [
2467 'targets' => [1],
2468 'assignees' => [3],
2469 ],
2470 ],
2471 40 => [
2472 'do first' => [
2473 'targets' => [],
2474 'assignees' => [3],
2475 ],
2476 'do second' => [
2477 'targets' => [1, 2],
2478 'assignees' => [3],
2479 ],
2480 ],
2481 41 => [
2482 'do first' => [
2483 'targets' => [],
2484 'assignees' => [3],
2485 ],
2486 'do second' => [
2487 'targets' => [1, 2],
2488 'assignees' => [3, 4],
2489 ],
2490 ],
2491 42 => [
2492 'do first' => [
2493 'targets' => [],
2494 'assignees' => [3],
2495 ],
2496 'do second' => [
2497 'targets' => [],
2498 'assignees' => [4],
2499 ],
2500 ],
2501 // And again but now start with 2 assignees
2502 43 => [
2503 'do first' => [
2504 'targets' => [],
2505 'assignees' => [3, 4],
2506 ],
2507 'do second' => [
2508 'targets' => [],
2509 'assignees' => [],
2510 ],
2511 ],
2512 44 => [
2513 'do first' => [
2514 'targets' => [],
2515 'assignees' => [3, 4],
2516 ],
2517 'do second' => [
2518 'targets' => [1],
2519 'assignees' => [],
2520 ],
2521 ],
2522 45 => [
2523 'do first' => [
2524 'targets' => [],
2525 'assignees' => [3, 4],
2526 ],
2527 'do second' => [
2528 'targets' => [1, 2],
2529 'assignees' => [],
2530 ],
2531 ],
2532 46 => [
2533 'do first' => [
2534 'targets' => [],
2535 'assignees' => [3, 4],
2536 ],
2537 'do second' => [
2538 'targets' => [],
2539 'assignees' => [3],
2540 ],
2541 ],
2542 47 => [
2543 'do first' => [
2544 'targets' => [],
2545 'assignees' => [3, 4],
2546 ],
2547 'do second' => [
2548 'targets' => [],
2549 'assignees' => [3, 4],
2550 ],
2551 ],
2552 48 => [
2553 'do first' => [
2554 'targets' => [],
2555 'assignees' => [3, 4],
2556 ],
2557 'do second' => [
2558 'targets' => [1],
2559 'assignees' => [3],
2560 ],
2561 ],
2562 49 => [
2563 'do first' => [
2564 'targets' => [],
2565 'assignees' => [3, 4],
2566 ],
2567 'do second' => [
2568 'targets' => [1, 2],
2569 'assignees' => [3],
2570 ],
2571 ],
2572 50 => [
2573 'do first' => [
2574 'targets' => [],
2575 'assignees' => [3, 4],
2576 ],
2577 'do second' => [
2578 'targets' => [1, 2],
2579 'assignees' => [3, 4],
2580 ],
2581 ],
2582 51 => [
2583 'do first' => [
2584 'targets' => [],
2585 'assignees' => [3, 4],
2586 ],
2587 'do second' => [
2588 'targets' => [],
2589 'assignees' => [4],
2590 ],
2591 ],
2592 ];
2593 }
2594
2595 /**
2596 * Test the returned activity ids when there are multiple "To" recipients.
2597 * Similar to testSendEmailWillReplaceTokensUniquelyForEachContact but we're
2598 * checking the activity ids returned from sendEmail.
2599 */
2600 public function testSendEmailWithMultipleToRecipients(): void {
2601 $contactId1 = $this->individualCreate(['first_name' => 'Aaaa', 'last_name' => 'Bbbb']);
2602 $contactId2 = $this->individualCreate(['first_name' => 'Cccc', 'last_name' => 'Dddd']);
2603
2604 // create a logged in USER since the code references it for sendEmail user.
2605 $loggedInUser = $this->createLoggedInUser();
2606 $contacts = $this->callAPISuccess('Contact', 'get', [
2607 'sequential' => 1,
2608 'id' => ['IN' => [$contactId1, $contactId2]],
2609 ]);
2610
2611 [$sent, $activityIds] = CRM_Activity_BAO_Activity::sendEmail(
2612 $contacts['values'],
2613 'a subject',
2614 'here is some text',
2615 '<p>here is some html</p>',
2616 $contacts['values'][0]['email'],
2617 $loggedInUser,
2618 $from = __FUNCTION__ . '@example.com',
2619 $attachments = NULL,
2620 $cc = NULL,
2621 $bcc = NULL,
2622 array_column($contacts['values'], 'id')
2623 );
2624
2625 // Get all activities for these contacts
2626 $result = $this->callAPISuccess('activity', 'get', [
2627 'sequential' => 1,
2628 'return' => ['target_contact_id'],
2629 'target_contact_id' => ['IN' => [$contactId1, $contactId2]],
2630 ]);
2631
2632 // There should be one activity created for each of the two contacts
2633 $this->assertEquals(2, $result['count']);
2634
2635 // Activity ids returned from sendEmail should match the ones returned from api call.
2636 $this->assertEquals($activityIds, array_column($result['values'], 'id'));
2637
2638 // Is it the right contacts?
2639 $this->assertEquals(
2640 [0 => [0 => $contactId1], 1 => [0 => $contactId2]],
2641 array_column($result['values'], 'target_contact_id')
2642 );
2643 $this->assertEquals(
2644 [0 => [$contactId1 => 'Bbbb, Aaaa'], 1 => [$contactId2 => 'Dddd, Cccc']],
2645 array_column($result['values'], 'target_contact_sort_name')
2646 );
2647 }
2648
2649 /**
2650 * @param $activityId
2651 *
2652 * @throws \API_Exception
2653 * @throws \Civi\API\Exception\UnauthorizedException
2654 */
2655 protected function validateActivity($activityId): void {
2656 $activity = Activity::get(FALSE)
2657 ->addSelect('activity_type_id', 'status_id', 'subject', 'details')
2658 ->addWhere('id', '=', $activityId)
2659 ->execute()->first();
2660
2661 $outBoundSmsActivityId = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'SMS');
2662 $activityStatusCompleted = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed');
2663 $this->assertEquals($outBoundSmsActivityId, $activity['activity_type_id'], 'Wrong activity type is set.');
2664 $this->assertEquals($activityStatusCompleted, $activity['status_id'], 'Expected activity status Completed.');
2665 $this->assertEquals('subject', $activity['subject'], 'Activity subject does not match.');
2666 $this->assertEquals('text', $activity['details'], 'Activity details does not match.');
2667 }
2668
2669 /**
2670 * @param int $contactId
2671 * @param bool $passPhoneTypeInContactDetails
2672 * @param $contactDetails
2673 *
2674 * @return array
2675 * @throws \CiviCRM_API3_Exception
2676 */
2677 protected function createMobilePhone(int $contactId, bool $passPhoneTypeInContactDetails, $contactDetails): array {
2678 $phone = civicrm_api3('Phone', 'create', [
2679 'contact_id' => $contactId,
2680 'phone' => 123456,
2681 'phone_type_id' => 'Mobile',
2682 ]);
2683 if ($passPhoneTypeInContactDetails) {
2684 $contactDetails[$contactId]['phone'] = $phone['values'][$phone['id']]['phone'];
2685 $contactDetails[$contactId]['phone_type_id'] = $phone['values'][$phone['id']]['phone_type_id'];
2686 }
2687 return $contactDetails;
2688 }
2689
2690 /**
2691 * Get a campaign id - creating one if need be.
2692 *
2693 * @return int
2694 */
2695 protected function getCampaignID() {
2696 if (!isset($this->ids['Campaign'][0])) {
2697 $this->ids['Campaign'][0] = $this->callAPISuccess('Campaign', 'create', [
2698 'title' => 'campaign',
2699 ])['id'];
2700 }
2701 return $this->ids['Campaign'][0];
2702 }
2703
2704 }