dev/core#2814 Fix activity:sendEmail to use renderTemplate
[civicrm-core.git] / tests / phpunit / CRM / Activity / BAO / ActivityTest.php
CommitLineData
6a488035 1<?php
0eea664b 2
ddeafe59
EM
3use Civi\Api4\Activity;
4
aba1cd8b
EM
5/**
6 * Class CRM_Activity_BAO_ActivityTest
acb109b7 7 * @group headless
aba1cd8b 8 */
6a488035 9class CRM_Activity_BAO_ActivityTest extends CiviUnitTestCase {
39b959db 10
5123cb11
SL
11 private $allowedContactsACL = [];
12
28743d2f 13 private $loggedInUserId = NULL;
14
15 private $someContacts = [];
16
e51dca64 17 /**
18 * Set up for test.
19 *
20 * @throws \CiviCRM_API3_Exception
21 */
5d345864 22 public function setUp():void {
6a488035 23 parent::setUp();
cbcedb39 24 $this->prepareForACLs();
9099cab3 25 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['view all contacts', 'access CiviCRM'];
1a7f0799 26 $this->setupForSmsTests();
6a488035
TO
27 }
28
cbcedb39 29 /**
30 * Clean up after tests.
e51dca64 31 *
ddeafe59 32 * @throws \API_Exception
e51dca64 33 * @throws \CRM_Core_Exception
34 * @throws \CiviCRM_API3_Exception
cbcedb39 35 */
594a9328 36 public function tearDown(): void {
9099cab3 37 $tablesToTruncate = [
824989b9
AS
38 'civicrm_activity',
39 'civicrm_activity_contact',
40 'civicrm_uf_match',
41 'civicrm_campaign',
42 'civicrm_email',
9099cab3 43 ];
6a488035 44 $this->quickCleanup($tablesToTruncate);
cbcedb39 45 $this->cleanUpAfterACLs();
1a7f0799 46 $this->setupForSmsTests(TRUE);
cbcedb39 47 parent::tearDown();
6a488035
TO
48 }
49
50 /**
fe482240 51 * Test case for create() method.
e51dca64 52 *
53 * @throws \CRM_Core_Exception
6a488035 54 */
00be9182 55 public function testCreate() {
f7969dcf 56 $contactId = $this->individualCreate();
6a488035 57
9099cab3 58 $params = [
6a488035
TO
59 'source_contact_id' => $contactId,
60 'subject' => 'Scheduling Meeting',
61 'activity_type_id' => 2,
9099cab3 62 ];
6a488035
TO
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
cbcedb39 70 // Now call create() to modify an existing Activity.
9099cab3 71 $params = [
6a488035
TO
72 'id' => $activityId,
73 'source_contact_id' => $contactId,
74 'subject' => 'Scheduling Interview',
75 'activity_type_id' => 3,
9099cab3 76 ];
6a488035
TO
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
93ac19cd 86 $this->contactDelete($contactId);
6a488035
TO
87 }
88
89 /**
fe482240
EM
90 * Test case for getContactActivity() method.
91 *
ddeafe59
EM
92 * getContactActivity() method get activities detail for given target contact
93 * id.
e51dca64 94 *
95 * @throws \CRM_Core_Exception
ddeafe59 96 * @throws \CiviCRM_API3_Exception
6a488035 97 */
00be9182 98 public function testGetContactActivity() {
f7969dcf 99 $contactId = $this->individualCreate();
9099cab3 100 $params = [
6a488035
TO
101 'first_name' => 'liz',
102 'last_name' => 'hurleey',
9099cab3 103 ];
f7969dcf 104 $targetContactId = $this->individualCreate($params);
6a488035 105
9099cab3 106 $params = [
6a488035
TO
107 'source_contact_id' => $contactId,
108 'subject' => 'Scheduling Meeting',
109 'activity_type_id' => 2,
9099cab3 110 'target_contact_id' => [$targetContactId],
6a488035 111 'activity_date_time' => date('Ymd'),
9099cab3 112 ];
6a488035 113
f7969dcf 114 $this->callAPISuccess('Activity', 'create', $params);
6a488035
TO
115
116 $activityId = $this->assertDBNotNull('CRM_Activity_DAO_Activity', 'Scheduling Meeting',
117 'id',
118 'subject', 'Database check for created activity.'
119 );
120
a59cecb1 121 // @todo - remove this deprecated functions
6a488035
TO
122 $activities = CRM_Activity_BAO_Activity::getContactActivity($targetContactId);
123
124 $this->assertEquals($activities[$activityId]['subject'], 'Scheduling Meeting', 'Verify activity subject is correct.');
125
93ac19cd 126 $this->contactDelete($contactId);
127 $this->contactDelete($targetContactId);
6a488035
TO
128 }
129
130 /**
fe482240
EM
131 * Test case for retrieve() method.
132 *
133 * Retrieve($params, $defaults) method return activity detail for given params
6a488035
TO
134 * and set defaults.
135 */
ddeafe59 136 public function testRetrieve(): void {
f7969dcf 137 $contactId = $this->individualCreate();
9099cab3 138 $params = [
6a488035
TO
139 'first_name' => 'liz',
140 'last_name' => 'hurleey',
9099cab3 141 ];
f7969dcf 142 $targetContactId = $this->individualCreate($params);
6a488035 143
9099cab3 144 $params = [
6a488035
TO
145 'source_contact_id' => $contactId,
146 'subject' => 'Scheduling Meeting',
147 'activity_type_id' => 2,
9099cab3 148 'target_contact_id' => [$targetContactId],
6a488035 149 'activity_date_time' => date('Ymd'),
9099cab3 150 ];
6a488035
TO
151
152 CRM_Activity_BAO_Activity::create($params);
153
cbcedb39 154 $this->assertDBNotNull('CRM_Activity_DAO_Activity', 'Scheduling Meeting', 'id',
6a488035
TO
155 'subject', 'Database check for created activity.'
156 );
157
cbcedb39 158 $this->assertDBNotNull('CRM_Activity_DAO_ActivityContact', $targetContactId,
f1c7b1f0 159 'id', 'contact_id',
6a488035
TO
160 'Database check for created activity target.'
161 );
162
9099cab3 163 $defaults = [];
6a488035
TO
164 $activity = CRM_Activity_BAO_Activity::retrieve($params, $defaults);
165
166 $this->assertEquals($activity->subject, 'Scheduling Meeting', 'Verify activity subject is correct.');
6a488035 167 $this->assertEquals($activity->activity_type_id, 2, 'Verify activity type id is correct.');
b2e56051 168 $this->assertEquals($defaults['source_contact_id'], $contactId, 'Verify source contact id is correct.');
6a488035
TO
169
170 $this->assertEquals($defaults['subject'], 'Scheduling Meeting', 'Verify activity subject is correct.');
6a488035
TO
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
93ac19cd 175 $this->contactDelete($contactId);
176 $this->contactDelete($targetContactId);
6a488035
TO
177 }
178
5123cb11
SL
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();
9099cab3 184 $params = [
5123cb11
SL
185 'first_name' => 'liz',
186 'last_name' => 'hurleey',
9099cab3 187 ];
5123cb11
SL
188 $targetContactId = $this->individualCreate($params);
189
9099cab3 190 $params = [
5123cb11
SL
191 'source_contact_id' => $contactId,
192 'subject' => 'Scheduling Meeting',
193 'activity_type_id' => 2,
9099cab3 194 'target_contact_id' => [$targetContactId],
5123cb11 195 'activity_date_time' => date('Ymd'),
9099cab3 196 ];
5123cb11
SL
197
198 CRM_Activity_BAO_Activity::create($params);
199
200 // set custom hook
9099cab3 201 $this->hookClass->setHook('civicrm_aclWhereClause', [$this, 'hook_civicrm_aclWhereClause']);
5123cb11 202
9099cab3 203 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
5123cb11 204
9099cab3 205 $this->allowedContactsACL = [$contactId];
5123cb11
SL
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);
995f30cc
SL
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']);
05704444 218 $this->cleanUpAfterACLs();
5123cb11
SL
219 }
220
146058da 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
6a488035 250 /**
fe482240
EM
251 * Test case for deleteActivity() method.
252 *
6a488035 253 * deleteActivity($params) method deletes activity for given params.
ddeafe59
EM
254 *
255 * @throws \CRM_Core_Exception
256 * @throws \CiviCRM_API3_Exception
6a488035 257 */
00be9182 258 public function testDeleteActivity() {
f7969dcf 259 $contactId = $this->individualCreate();
9099cab3 260 $params = [
6a488035
TO
261 'first_name' => 'liz',
262 'last_name' => 'hurleey',
9099cab3 263 ];
f7969dcf 264 $targetContactId = $this->individualCreate($params);
6a488035 265
9099cab3 266 $params = [
6a488035
TO
267 'source_contact_id' => $contactId,
268 'source_record_id' => $contactId,
269 'subject' => 'Scheduling Meeting',
270 'activity_type_id' => 2,
9099cab3 271 'target_contact_id' => [$targetContactId],
6a488035 272 'activity_date_time' => date('Ymd'),
9099cab3 273 ];
6a488035
TO
274
275 CRM_Activity_BAO_Activity::create($params);
276
cbcedb39 277 $this->assertDBNotNull('CRM_Activity_DAO_Activity', 'Scheduling Meeting', 'id',
6a488035
TO
278 'subject', 'Database check for created activity.'
279 );
280
cbcedb39 281 $this->assertDBNotNull('CRM_Activity_DAO_ActivityContact', $targetContactId,
f1c7b1f0 282 'id', 'contact_id',
6a488035
TO
283 'Database check for created activity target.'
284 );
4286fa45
SL
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 }
9099cab3 303 $params = [
6a488035
TO
304 'source_contact_id' => $contactId,
305 'source_record_id' => $contactId,
306 'subject' => 'Scheduling Meeting',
307 'activity_type_id' => 2,
9099cab3 308 ];
6a488035 309
93ac19cd 310 CRM_Activity_BAO_Activity::deleteActivity($params);
6a488035 311
93ac19cd 312 $this->assertDBNull('CRM_Activity_DAO_Activity', 'Scheduling Meeting', 'id',
6a488035
TO
313 'subject', 'Database check for deleted activity.'
314 );
93ac19cd 315 $this->contactDelete($contactId);
316 $this->contactDelete($targetContactId);
6a488035
TO
317 }
318
319 /**
2b810608 320 * Test case for deleteActivityContact() method.
6a488035 321 */
00be9182 322 public function testDeleteActivityTarget() {
f7969dcf 323 $contactId = $this->individualCreate();
9099cab3 324 $params = [
6a488035
TO
325 'first_name' => 'liz',
326 'last_name' => 'hurleey',
9099cab3 327 ];
f7969dcf 328 $targetContactId = $this->individualCreate($params);
6a488035 329
9099cab3 330 $params = [
6a488035
TO
331 'source_contact_id' => $contactId,
332 'subject' => 'Scheduling Meeting',
333 'activity_type_id' => 2,
9099cab3 334 'target_contact_id' => [$targetContactId],
6a488035 335 'activity_date_time' => date('Ymd'),
9099cab3 336 ];
6a488035
TO
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
cbcedb39 344 $this->assertDBNotNull('CRM_Activity_DAO_ActivityContact', $targetContactId,
f1c7b1f0 345 'id', 'contact_id',
6a488035
TO
346 'Database check for created activity target.'
347 );
348
2517d079 349 CRM_Activity_BAO_Activity::deleteActivityContact($activityId, 3);
6a488035 350
f1c7b1f0
DL
351 $this->assertDBNull('CRM_Activity_DAO_ActivityContact', $targetContactId, 'id',
352 'contact_id', 'Database check for deleted activity target.'
6a488035
TO
353 );
354
93ac19cd 355 $this->contactDelete($contactId);
356 $this->contactDelete($targetContactId);
6a488035
TO
357 }
358
359 /**
fe482240
EM
360 * Test case for deleteActivityAssignment() method.
361 *
ddeafe59
EM
362 * deleteActivityAssignment($activityId) method deletes activity assignment
363 * for given activity id.
364 *
365 * @throws \CRM_Core_Exception
366 * @throws \CiviCRM_API3_Exception
6a488035 367 */
ddeafe59 368 public function testDeleteActivityAssignment(): void {
f7969dcf 369 $contactId = $this->individualCreate();
9099cab3 370 $params = [
6a488035
TO
371 'first_name' => 'liz',
372 'last_name' => 'hurleey',
9099cab3 373 ];
f7969dcf 374 $assigneeContactId = $this->individualCreate($params);
6a488035 375
9099cab3 376 $params = [
6a488035
TO
377 'source_contact_id' => $contactId,
378 'subject' => 'Scheduling Meeting',
379 'activity_type_id' => 2,
9099cab3 380 'assignee_contact_id' => [$assigneeContactId],
6a488035 381 'activity_date_time' => date('Ymd'),
9099cab3 382 ];
6a488035
TO
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
cbcedb39 390 $this->assertDBNotNull('CRM_Activity_DAO_ActivityContact',
f1c7b1f0 391 $assigneeContactId, 'id', 'contact_id',
6a488035
TO
392 'Database check for created activity assignment.'
393 );
394
2517d079 395 CRM_Activity_BAO_Activity::deleteActivityContact($activityId, 1);
6a488035 396
f1c7b1f0
DL
397 $this->assertDBNull('CRM_Activity_DAO_ActivityContact', $assigneeContactId, 'id',
398 'contact_id', 'Database check for deleted activity assignment.'
6a488035
TO
399 );
400
93ac19cd 401 $this->contactDelete($contactId);
402 $this->contactDelete($assigneeContactId);
6a488035
TO
403 }
404
405 /**
5161bb0c 406 * Test getActivities BAO method for getting count.
3e120a63 407 *
6a488035 408 */
cbcedb39 409 public function testGetActivitiesCountForAdminDashboard() {
3e120a63
RLAR
410 // Reset to default
411 $this->setShowCaseActivitiesInCore(FALSE);
75d842f8 412 $this->setUpForActivityDashboardTests();
3e120a63
RLAR
413 $this->addCaseWithActivity();
414 CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'access all cases and activities';
415
6ab43e1b 416 $activityCount = CRM_Activity_BAO_Activity::getActivitiesCount($this->_params);
75d842f8 417 $this->assertEquals(8, $activityCount);
3e120a63
RLAR
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);
6a488035
TO
425 }
426
427 /**
5161bb0c 428 * Test getActivities BAO method for getting count
3e120a63 429 *
6a488035 430 */
00be9182 431 public function testGetActivitiesCountforNonAdminDashboard() {
3e120a63
RLAR
432 // Reset to default
433 $this->setShowCaseActivitiesInCore(FALSE);
a6c2ebdc 434 $this->createTestActivities();
3e120a63
RLAR
435 $this->addCaseWithActivity();
436 CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'access all cases and activities';
6a488035 437
9099cab3 438 $params = [
6a488035
TO
439 'contact_id' => 9,
440 'admin' => FALSE,
441 'caseId' => NULL,
442 'context' => 'home',
443 'activity_type_id' => NULL,
39b959db
SL
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'),
6a488035
TO
446 'offset' => 0,
447 'rowCount' => 0,
448 'sort' => NULL,
9099cab3 449 ];
6a488035 450
6a488035 451 //since we are loading activities from dataset, we know total number of activities for this contact
3e120a63
RLAR
452 // 5 activities ( 2 scheduled, 3 Completed, 1 Scheduled Case activity ),
453 // note that dashboard shows only scheduled activities
6ab43e1b 454 $this->assertEquals(2, CRM_Activity_BAO_Activity::getActivitiesCount($params));
3e120a63 455
ddeafe59 456 // If we're showing case activities, we expect to see one more (the scheduled meeting)...
3e120a63
RLAR
457 $this->setShowCaseActivitiesInCore(TRUE);
458 $this->assertEquals(3, CRM_Activity_BAO_Activity::getActivitiesCount($params));
459 // Reset to default
460 $this->setShowCaseActivitiesInCore(FALSE);
6a488035
TO
461 }
462
463 /**
5161bb0c 464 * Test getActivities BAO method for getting count
3e120a63 465 *
6a488035 466 */
00be9182 467 public function testGetActivitiesCountforContactSummary() {
3e120a63
RLAR
468 // Reset to default
469 $this->setShowCaseActivitiesInCore(FALSE);
a6c2ebdc 470 $this->createTestActivities();
3e120a63
RLAR
471 $this->addCaseWithActivity();
472 CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'access all cases and activities';
6a488035 473
9099cab3 474 $params = [
6a488035
TO
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,
9099cab3 483 ];
6a488035
TO
484
485 //since we are loading activities from dataset, we know total number of activities for this contact
3e120a63 486 // 5 activities
6ab43e1b 487 $this->assertEquals(5, CRM_Activity_BAO_Activity::getActivitiesCount($params));
3e120a63
RLAR
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);
6a488035
TO
494 }
495
23289ddd 496 /**
497 * CRM-18706 - Test Include/Exclude Activity Filters
498 */
499 public function testActivityFilters() {
a6c2ebdc 500 $this->createTestActivities();
c3a9ccbb
JP
501 Civi::settings()->set('preserve_activity_tab_filter', 1);
502 $this->createLoggedInUser();
23289ddd 503
504 global $_GET;
9099cab3 505 $_GET = [
23289ddd 506 'cid' => 9,
507 'context' => 'activity',
508 'activity_type_id' => 1,
9099cab3
CW
509 ];
510 $expectedFilters = [
c3a9ccbb 511 'activity_type_filter_id' => 1,
9099cab3 512 ];
23289ddd 513
a413c3db 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 }
c3a9ccbb
JP
521 //Assert whether filters are correctly set.
522 $this->checkArrayEquals($expectedFilters, $activityFilter);
23289ddd 523 // This should include activities of type Meeting only.
cbcedb39 524 foreach ($activities['data'] as $value) {
275686a3 525 $this->assertStringContainsString('Meeting', $value['activity_type']);
23289ddd 526 }
527 unset($_GET['activity_type_id']);
528
c3a9ccbb 529 $_GET['activity_type_exclude_id'] = $expectedFilters['activity_type_exclude_filter_id'] = 1;
a413c3db 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 }
c1d3e301 537 $this->assertEquals(['activity_type_exclude_filter_id' => 1], $activityFilter);
23289ddd 538 // None of the activities should be of type Meeting.
cbcedb39 539 foreach ($activities['data'] as $value) {
275686a3 540 $this->assertStringNotContainsString('Meeting', $value['activity_type']);
23289ddd 541 }
542 }
543
6a488035 544 /**
5161bb0c 545 * Test getActivities BAO method for getting count
6a488035 546 */
00be9182 547 public function testGetActivitiesCountforContactSummaryWithNoActivities() {
a6c2ebdc 548 $this->createTestActivities();
6a488035 549
9099cab3 550 $params = [
6a488035
TO
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,
9099cab3 559 ];
6a488035
TO
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
6ab43e1b 563 $this->assertEquals(0, CRM_Activity_BAO_Activity::getActivitiesCount($params));
6a488035
TO
564 }
565
566 /**
eceb18cc 567 * Test getActivities BAO method.
6a488035 568 */
cbcedb39 569 public function testGetActivitiesForAdminDashboard() {
3e120a63 570 $this->setShowCaseActivitiesInCore(FALSE);
75d842f8 571 $this->setUpForActivityDashboardTests();
3e120a63
RLAR
572 $this->addCaseWithActivity();
573 CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'access all cases and activities';
574
6ab43e1b 575 $activitiesNew = CRM_Activity_BAO_Activity::getActivities($this->_params);
576 // $this->assertEquals($activities, $activitiesDeprecatedFn);
6a488035
TO
577
578 //since we are loading activities from dataset, we know total number of activities
5161bb0c 579 // with no contact ID and there should be 8 schedule activities shown on dashboard
6a488035 580 $count = 8;
9099cab3 581 foreach ([$activitiesNew] as $activities) {
a413c3db 582 $this->assertCount($count, $activities);
6a488035 583
6ab43e1b 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 }
6a488035 589 }
3e120a63
RLAR
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);
a413c3db 594 $this->assertCount(9, $activitiesNew);
3e120a63 595 // Scan through to find the meeting.
a413c3db 596 $this->assertContains('test meeting activity', array_column($activitiesNew, 'subject'), "failed to find scheduled case Meeting activity");
3e120a63
RLAR
597 // Reset to default
598 $this->setShowCaseActivitiesInCore(FALSE);
6a488035
TO
599 }
600
75d842f8 601 /**
602 * Test getActivities BAO method.
603 */
604 public function testGetActivitiesForAdminDashboardNoViewContacts() {
9099cab3 605 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
75d842f8 606 $this->setUpForActivityDashboardTests();
9099cab3 607 foreach ([CRM_Activity_BAO_Activity::getActivities($this->_params)] as $activities) {
6ab43e1b 608 // Skipped until we get back to the upgraded version properly.
a413c3db 609 $this->assertCount(0, $activities);
6ab43e1b 610 }
26583d3e 611 }
612
613 /**
614 * Test getActivities BAO method.
615 */
616 public function testGetActivitiesForAdminDashboardAclLimitedViewContacts() {
9099cab3
CW
617 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
618 $this->allowedContacts = [1, 3, 4, 5];
619 $this->hookClass->setHook('civicrm_aclWhereClause', [$this, 'aclWhereMultipleContacts']);
26583d3e 620 $this->setUpForActivityDashboardTests();
6e793248 621 $this->assertEquals(7, count(CRM_Activity_BAO_Activity::getActivities($this->_params)));
75d842f8 622 }
623
6a488035 624 /**
eceb18cc 625 * Test getActivities BAO method.
6a488035 626 */
00be9182 627 public function testGetActivitiesforNonAdminDashboard() {
3e120a63 628 $this->setShowCaseActivitiesInCore(FALSE);
a6c2ebdc 629 $this->createTestActivities();
3e120a63
RLAR
630 $this->addCaseWithActivity();
631 CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'access all cases and activities';
6a488035
TO
632
633 $contactID = 9;
9099cab3 634 $params = [
6a488035
TO
635 'contact_id' => $contactID,
636 'admin' => FALSE,
637 'caseId' => NULL,
638 'context' => 'home',
639 'activity_type_id' => NULL,
39b959db
SL
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'),
6a488035
TO
642 'offset' => 0,
643 'rowCount' => 0,
644 'sort' => NULL,
9099cab3 645 ];
6a488035 646
9099cab3 647 foreach ([CRM_Activity_BAO_Activity::getActivities($params)] as $activities) {
6ab43e1b 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));
6a488035 652
6ab43e1b 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.');
6a488035 657
6ab43e1b 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 }
6a488035
TO
664 }
665 }
3e120a63
RLAR
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);
6a488035
TO
677 }
678
36d2f4d5 679 /**
680 * Test target contact count.
681 */
682 public function testTargetCountforContactSummary() {
683 $targetCount = 5;
684 $contactId = $this->individualCreate();
9099cab3 685 $targetContactIDs = [];
36d2f4d5 686 for ($i = 0; $i < $targetCount; $i++) {
9099cab3 687 $targetContactIDs[] = $this->individualCreate([], $i);
36d2f4d5 688 }
cbcedb39 689 // Create activities with 5 target contacts.
9099cab3 690 $activityParams = [
36d2f4d5 691 'source_contact_id' => $contactId,
692 'target_contact_id' => $targetContactIDs,
9099cab3 693 ];
36d2f4d5 694 $this->activityCreate($activityParams);
695
9099cab3 696 $params = [
36d2f4d5 697 'contact_id' => $contactId,
698 'context' => 'activity',
9099cab3 699 ];
c2ce41b6 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]);
36d2f4d5 706 }
707
a6c2ebdc 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
6a488035 732 /**
eceb18cc 733 * Test getActivities BAO method.
6a488035 734 */
c2ce41b6 735 public function testGetActivitiesForContactSummary() {
3e120a63
RLAR
736 // Reset to default
737 $this->setShowCaseActivitiesInCore(FALSE);
a6c2ebdc 738 $this->createTestActivities();
3e120a63
RLAR
739 $this->addCaseWithActivity();
740 CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'access all cases and activities';
6a488035
TO
741
742 $contactID = 9;
9099cab3 743 $params = [
6a488035
TO
744 'contact_id' => $contactID,
745 'admin' => FALSE,
746 'caseId' => NULL,
747 'context' => 'activity',
748 'activity_type_id' => NULL,
749 'offset' => 0,
750 'rowCount' => 0,
9099cab3 751 ];
6a488035
TO
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;
c2ce41b6 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.');
6a488035 760
c2ce41b6 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 }
6a488035 767
c2ce41b6 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 }
6ab43e1b 781
c2ce41b6 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']);
6a488035
TO
787 }
788 }
3e120a63
RLAR
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);
6a488035
TO
799 }
800
801 /**
eceb18cc 802 * Test getActivities BAO method.
6a488035 803 */
5161bb0c 804 public function testGetActivitiesforContactSummaryWithActivities() {
3e120a63
RLAR
805 // Reset to default
806 $this->setShowCaseActivitiesInCore(FALSE);
a6c2ebdc 807 $this->createTestActivities();
6a488035 808
6ab43e1b 809 // parameters for different test cases, check each array key for the specific test-case
9099cab3
CW
810 $testCases = [
811 'with-no-activity' => [
812 'params' => [
5161bb0c 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,
9099cab3
CW
821 ],
822 ],
823 'with-activity' => [
824 'params' => [
5161bb0c 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,
9099cab3
CW
833 ],
834 ],
835 'with-activity_type' => [
836 'params' => [
5161bb0c 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,
9099cab3
CW
845 ],
846 ],
847 'exclude-all-activity_type' => [
848 'params' => [
5161bb0c 849 'contact_id' => 3,
850 'admin' => FALSE,
851 'caseId' => NULL,
852 'context' => 'home',
9099cab3 853 'activity_type_exclude_id' => [1, 2],
5161bb0c 854 'offset' => 0,
855 'rowCount' => 0,
856 'sort' => NULL,
9099cab3
CW
857 ],
858 ],
859 'sort-by-subject' => [
860 'params' => [
5161bb0c 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',
9099cab3
CW
869 ],
870 ],
871 ];
5161bb0c 872
873 foreach ($testCases as $caseName => $testCase) {
6e793248 874 $activityCount = CRM_Activity_BAO_Activity::getActivitiesCount($testCase['params']);
6ab43e1b 875 $activitiesNew = CRM_Activity_BAO_Activity::getActivities($testCase['params']);
876
9099cab3 877 foreach ([$activitiesNew] as $activities) {
6ab43e1b 878 //$this->assertEquals($activityCount, CRM_Activity_BAO_Activity::getActivitiesCount($testCase['params']));
ddeafe59 879 if ($caseName === 'with-no-activity') {
6ab43e1b 880 $this->assertEquals(0, count($activities));
881 $this->assertEquals(0, $activityCount);
882 }
ddeafe59 883 elseif ($caseName === 'with-activity') {
6ab43e1b 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 }
ddeafe59 892 elseif ($caseName === 'with-activity_type') {
6ab43e1b 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 }
ddeafe59 904 if ($caseName === 'exclude-all-activity_type') {
6ab43e1b 905 $this->assertEquals(0, count($activities));
906 $this->assertEquals(0, $activityCount);
907 }
ddeafe59 908 if ($caseName === 'sort-by-subject') {
6ab43e1b 909 $this->assertEquals(3, count($activities));
910 $this->assertEquals(3, $activityCount);
911 // activities should be order by 'subject DESC'
9099cab3 912 $subjectOrder = [
6ab43e1b 913 'subject 8',
914 'subject 7',
915 'subject 1',
9099cab3 916 ];
6ab43e1b 917 $count = 0;
918 foreach ($activities as $activity) {
919 $this->assertEquals($subjectOrder[$count], $activity['subject']);
920 $count++;
921 }
5161bb0c 922 }
923 }
924 }
6a488035 925 }
96025800 926
49d4d222 927 /**
928 * CRM-20793 : Test getActivities by using activity date and status filter
ddeafe59
EM
929 *
930 * @throws \CRM_Core_Exception
49d4d222 931 */
ddeafe59 932 public function testByActivityDateAndStatus(): void {
6e793248 933 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['view all contacts', 'access CiviCRM'];
a6c2ebdc 934 $this->createTestActivities();
49d4d222 935
936 // activity IDs catagorised by date
9099cab3
CW
937 $lastWeekActivities = [1, 2, 3];
938 $todayActivities = [4, 5, 6, 7];
939 $lastTwoMonthsActivities = [8, 9, 10, 11];
940 $lastOrNextYearActivities = [12, 13, 14, 15, 16];
49d4d222 941
942 // date values later used to set activity date value
943 $lastWeekDate = date('YmdHis', strtotime('1 week ago'));
944 $today = date('YmdHis');
a6d192c8 945 $lastTwoMonthAgoDate = date('YmdHis', strtotime('2 months ago'));
daff7f26 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
ddeafe59 948 $lastOrNextYearDate = (date('M') === 'Jan') ? date('YmdHis', strtotime('+1 year')) : date('YmdHis', strtotime('1 year ago'));
49d4d222 949 for ($i = 1; $i <= 16; $i++) {
950 if (in_array($i, $lastWeekActivities)) {
951 $date = $lastWeekDate;
952 }
a6d192c8 953 elseif (in_array($i, $lastTwoMonthsActivities)) {
954 $date = $lastTwoMonthAgoDate;
49d4d222 955 }
daff7f26 956 elseif (in_array($i, $lastOrNextYearActivities)) {
957 $date = $lastOrNextYearDate;
49d4d222 958 }
959 elseif (in_array($i, $todayActivities)) {
960 $date = $today;
961 }
9099cab3 962 $this->callAPISuccess('Activity', 'create', [
49d4d222 963 'id' => $i,
964 'activity_date_time' => $date,
9099cab3 965 ]);
49d4d222 966 }
967
968 // parameters for different test cases, check each array key for the specific test-case
9099cab3
CW
969 $testCases = [
970 'todays-activity' => [
971 'params' => [
49d4d222 972 'contact_id' => 1,
973 'admin' => TRUE,
974 'caseId' => NULL,
975 'context' => 'activity',
6e793248 976 'activity_date_time_relative' => 'this.day',
49d4d222 977 'activity_type_id' => NULL,
978 'offset' => 0,
979 'rowCount' => 0,
980 'sort' => NULL,
9099cab3
CW
981 ],
982 ],
983 'todays-activity-filtered-by-range' => [
984 'params' => [
49d4d222 985 'contact_id' => 1,
986 'admin' => TRUE,
987 'caseId' => NULL,
988 'context' => 'activity',
6e793248 989 'activity_date_time_low' => date('Y/m/d', strtotime('yesterday')),
990 'activity_date_time_high' => date('Y/m/d'),
49d4d222 991 'activity_type_id' => NULL,
992 'offset' => 0,
993 'rowCount' => 0,
994 'sort' => NULL,
9099cab3
CW
995 ],
996 ],
997 'last-week-activity' => [
998 'params' => [
49d4d222 999 'contact_id' => 1,
1000 'admin' => TRUE,
1001 'caseId' => NULL,
1002 'context' => 'activity',
6e793248 1003 'activity_date_time_relative' => 'previous.week',
49d4d222 1004 'activity_type_id' => NULL,
1005 'offset' => 0,
1006 'rowCount' => 0,
1007 'sort' => NULL,
9099cab3
CW
1008 ],
1009 ],
1010 'this-quarter-activity' => [
1011 'params' => [
49d4d222 1012 'contact_id' => 1,
1013 'admin' => TRUE,
1014 'caseId' => NULL,
1015 'context' => 'activity',
6e793248 1016 'activity_date_time_relative' => 'this.quarter',
49d4d222 1017 'activity_type_id' => NULL,
1018 'offset' => 0,
1019 'rowCount' => 0,
1020 'sort' => NULL,
9099cab3
CW
1021 ],
1022 ],
1023 'activity-of-all-statuses' => [
1024 'params' => [
49d4d222 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,
9099cab3
CW
1034 ],
1035 ],
1036 ];
49d4d222 1037
1038 foreach ($testCases as $caseName => $testCase) {
6e793248 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);
49d4d222 1044
ddeafe59 1045 if ($caseName === 'todays-activity' || $caseName === 'todays-activity-filtered-by-range') {
6e793248 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));
49d4d222 1050 }
ddeafe59 1051 elseif ($caseName === 'last-week-activity') {
6e793248 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);
49d4d222 1056 }
ddeafe59 1057 elseif ($caseName === 'lhis-quarter-activity') {
a6d192c8 1058 $this->assertEquals(count($lastTwoMonthsActivities), $activityCount);
6e793248 1059 $this->assertEquals(count($lastTwoMonthsActivities), count($activities));
a6d192c8 1060 $this->checkArrayEquals($lastTwoMonthsActivities, $activityIDs);
49d4d222 1061 }
ddeafe59 1062 elseif ($caseName === 'last-or-next-year-activity') {
daff7f26 1063 $this->assertEquals(count($lastOrNextYearActivities), $activityCount);
6e793248 1064 $this->assertEquals(count($lastOrNextYearActivities), count($activities));
daff7f26 1065 $this->checkArrayEquals($lastOrNextYearActivities, $activityIDs);
49d4d222 1066 }
ddeafe59 1067 elseif ($caseName === 'activity-of-all-statuses') {
6e793248 1068 $this->assertEquals(3, $activityCount);
1069 $this->assertEquals(3, count($activities));
49d4d222 1070 }
1071 }
1072 }
1073
afc82ed3 1074 /**
1075 * @dataProvider getActivityDateData
ddeafe59
EM
1076 *
1077 * @param $params
1078 * @param $expected
1079 *
1080 * @throws \CRM_Core_Exception
1081 * @throws \CiviCRM_API3_Exception
afc82ed3 1082 */
ddeafe59 1083 public function testActivityRelativeDateFilter($params, $expected): void {
afc82ed3 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 }
6e793248 1096 $activitiesDep = CRM_Activity_BAO_Activity::getActivities($params);
1097 $activityCount = CRM_Activity_BAO_Activity::getActivitiesCount($params);
afc82ed3 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'),
39b959db 1133 ],
afc82ed3 1134 ],
1135 ];
1136 }
1137
3cf1fae9 1138 /**
1139 * CRM-20308: Test from email address when a 'copy of Activity' event occur
ddeafe59
EM
1140 *
1141 * @throws \CRM_Core_Exception
1142 * @throws \CiviCRM_API3_Exception
3cf1fae9 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();
9099cab3 1148 $sourceContactParams = [
3cf1fae9 1149 'first_name' => 'liz',
1150 'last_name' => 'hurleey',
0952020a 1151 'email' => 'liz@testemail.com',
9099cab3 1152 ];
3cf1fae9 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
9099cab3 1157 $params = [
3cf1fae9 1158 'source_contact_id' => $sourceContactID,
0952020a
EM
1159 'subject' => 'Scheduling Meeting',
1160 'activity_type_id' => 'Meeting',
9099cab3 1161 'assignee_contact_id' => [$assigneeContactId],
0952020a 1162 'activity_date_time' => 'now',
9099cab3 1163 ];
3cf1fae9 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']);
ddeafe59 1168 $expectedFromAddress = sprintf('%s <%s>', $sourceDisplayName, $sourceContactParams['email']);
3cf1fae9 1169 $this->assertEquals($expectedFromAddress, $formAddress);
3cf1fae9 1170
1171 // Case 2: System Default From Address
1172 // but first erase the email address of existing source contact ID
9099cab3 1173 $withoutEmailParams = [
3cf1fae9 1174 'email' => '',
9099cab3 1175 ];
3cf1fae9 1176 $sourceContactID = $this->individualCreate($withoutEmailParams);
9099cab3 1177 $params = [
3cf1fae9 1178 'source_contact_id' => $sourceContactID,
0952020a 1179 'subject' => 'Scheduling Meeting 2',
3cf1fae9 1180 'activity_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Meeting'),
1181 'activity_date_time' => date('Ymd'),
9099cab3 1182 ];
3cf1fae9 1183 $activity = $this->callAPISuccess('Activity', 'create', $params);
1184 // fetch domain info
9099cab3 1185 $domainInfo = $this->callAPISuccess('Domain', 'getsingle', ['id' => CRM_Core_Config::domainID()]);
3cf1fae9 1186
1187 $formAddress = CRM_Case_BAO_Case::getReceiptFrom($activity['id']);
1188 if (!empty($domainInfo['from_email'])) {
0952020a 1189 $expectedFromAddress = sprintf('%s <%s>', $domainInfo['from_name'], $domainInfo['from_email']);
3cf1fae9 1190 }
1191 // Case 3: fetch default Organization Contact email address
1192 elseif (!empty($domainInfo['domain_email'])) {
0952020a 1193 $expectedFromAddress = sprintf('%s <%s>', $domainInfo['name'], $domainInfo['domain_email']);
3cf1fae9 1194 }
e88906fc 1195 $this->assertEquals($expectedFromAddress, $formAddress);
3cf1fae9 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
75d842f8 1201 /**
1202 * Set up for testing activity queries.
1203 */
1204 protected function setUpForActivityDashboardTests() {
a6c2ebdc 1205 $this->createTestActivities();
75d842f8 1206
9099cab3 1207 $this->_params = [
75d842f8 1208 'contact_id' => NULL,
1209 'admin' => TRUE,
1210 'caseId' => NULL,
1211 'context' => 'home',
1212 'activity_type_id' => NULL,
39b959db
SL
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'),
75d842f8 1215 'offset' => 0,
1216 'rowCount' => 0,
1217 'sort' => NULL,
9099cab3 1218 ];
75d842f8 1219 }
1220
e5914f86
EM
1221 /**
1222 * @throws \CRM_Core_Exception
1223 * @throws \CiviCRM_API3_Exception
1224 */
1225 public function testSendEmailBasic(): void {
824989b9
AS
1226 $contactId = $this->individualCreate();
1227
1228 // create a logged in USER since the code references it for sendEmail user.
bbd99e79 1229 $loggedInUser = $this->createLoggedInUser();
824989b9 1230
e5914f86
EM
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)]);
9099cab3 1243 $contactDetailsIntersectKeys = [
824989b9
AS
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' => '',
9099cab3
CW
1252 ];
1253 $contactDetails = [
824989b9 1254 array_intersect_key($contact, $contactDetailsIntersectKeys),
9099cab3 1255 ];
824989b9
AS
1256
1257 $subject = __FUNCTION__ . ' subject';
1258 $html = __FUNCTION__ . ' html';
1259 $text = __FUNCTION__ . ' text';
1260 $userID = $loggedInUser;
1261
e5914f86 1262 [$sent, $activity_ids] = CRM_Activity_BAO_Activity::sendEmail(
824989b9
AS
1263 $contactDetails,
1264 $subject,
1265 $text,
1266 $html,
1267 $contact['email'],
1268 $userID,
1269 $from = __FUNCTION__ . '@example.com'
1270 );
1271
e5914f86 1272 $activity = $this->callAPISuccessGetSingle('Activity', ['id' => $activity_ids[0], 'return' => ['details', 'subject']]);
824989b9
AS
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
9099cab3
CW
1292 $contact = $this->civicrm_api('contact', 'getsingle', ['id' => $contactId, 'version' => $this->_apiversion]);
1293 $contactDetailsIntersectKeys = [
824989b9
AS
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' => '',
9099cab3
CW
1302 ];
1303 $contactDetails = [
824989b9 1304 array_intersect_key($contact, $contactDetailsIntersectKeys),
9099cab3 1305 ];
824989b9
AS
1306
1307 // Create a campaign.
9099cab3 1308 $result = $this->civicrm_api('Campaign', 'create', [
824989b9
AS
1309 'version' => $this->_apiversion,
1310 'title' => __FUNCTION__ . ' campaign',
9099cab3 1311 ]);
824989b9
AS
1312 $campaign_id = $result['id'];
1313
1314 $subject = __FUNCTION__ . ' subject';
1315 $html = __FUNCTION__ . ' html';
1316 $text = __FUNCTION__ . ' text';
1317 $userID = $loggedInUser;
1318
ddeafe59 1319 [$sent, $activity_ids] = $email_result = CRM_Activity_BAO_Activity::sendEmail(
824989b9
AS
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,
cb5d08cd 1332 NULL,
824989b9
AS
1333 $campaign_id
1334 );
13ecf776 1335 $activity = $this->civicrm_api('activity', 'getsingle', ['id' => $activity_ids[0], 'version' => $this->_apiversion]);
824989b9
AS
1336 $this->assertEquals($activity['campaign_id'], $campaign_id, 'Activity campaign_id does not match.');
1337 }
1338
63483feb 1339 /**
63483feb
MM
1340 */
1341 public function testSendSMSWithoutPermission() {
1342 $dummy = NULL;
1343 $session = CRM_Core_Session::singleton();
9099cab3 1344 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
275686a3
SL
1345 $this->expectException(CRM_Core_Exception::class);
1346 $this->expectExceptionMessage('You do not have the \'send SMS\' permission');
63483feb
MM
1347 CRM_Activity_BAO_Activity::sendSMS(
1348 $dummy,
1349 $dummy,
1350 $dummy,
1351 $dummy,
1352 $session->get('userID')
1353 );
1354 }
1355
ddeafe59
EM
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");
1a7f0799
MW
1367 }
1368
ddeafe59
EM
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");
1a7f0799
MW
1378 }
1379
ddeafe59
EM
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");
1a7f0799
MW
1389 }
1390
a9b7ee41 1391 /**
ddeafe59 1392 * Test that when a number is specified in the To Param of the SMS provider parameters that an SMS is sent
a9b7ee41
SL
1393 * @see dev/core/#273
1394 */
ddeafe59
EM
1395 public function testSendSMSMobileInToProviderParam(): void {
1396 $sent = $this->createSendSmsTest(TRUE, 2, TRUE);
1397 $this->assertEquals(TRUE, $sent[0], 'Expected sent should be true');
a9b7ee41
SL
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 */
ddeafe59
EM
1404 public function testSendSMSMobileInToProviderParamWithDoNotSMS(): void {
1405 $sent = $this->createSendSmsTest(FALSE, 2, TRUE, ['do_not_sms' => 1]);
a9b7ee41 1406 foreach ($sent as $error) {
4a0e3fe7 1407 $this->assertEquals('Contact Does not accept SMS', $error);
a9b7ee41 1408 }
ddeafe59 1409 $this->assertCount(1, $sent, 'Expected sent should a PEAR Error');
a9b7ee41
SL
1410 }
1411
1a7f0799 1412 /**
ddeafe59
EM
1413 * @param bool $expectSuccess
1414 * @param int $phoneType (0=no phone, phone_type option group (1=fixed,
1415 * 2=mobile)
a9b7ee41
SL
1416 * @param bool $passPhoneTypeInContactDetails
1417 * @param array $additionalContactParams additional contact creation params
ddeafe59
EM
1418 *
1419 * @return array
1420 * @throws \API_Exception
1421 * @throws \CRM_Core_Exception
1422 * @throws \CiviCRM_API3_Exception
1423 * @throws \Civi\API\Exception\UnauthorizedException
1a7f0799 1424 */
ddeafe59 1425 public function createSendSmsTest(bool $expectSuccess = TRUE, int $phoneType = 0, bool $passPhoneTypeInContactDetails = FALSE, array $additionalContactParams = []): array {
9099cab3 1426 $provider = civicrm_api3('SmsProvider', 'create', [
ddeafe59
EM
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,
9099cab3 1436 ]);
a9b7ee41 1437
1a7f0799
MW
1438 $smsProviderParams['provider_id'] = $provider['id'];
1439
1440 // Create a contact
1441 $contactId = $this->individualCreate();
a9b7ee41
SL
1442 if (!empty($additionalContactParams)) {
1443 $this->callAPISuccess('contact', 'create', ['id' => $contactId] + $additionalContactParams);
1444 }
ddeafe59 1445 $contactsResult = $this->callApiSuccess('Contact', 'get', ['id' => $contactId, 'return' => ['id', 'phone_type_id', 'do_not_sms']]);
1a7f0799
MW
1446 $contactDetails = $contactsResult['values'];
1447
1448 // Get contactIds from contact details
1449 foreach ($contactDetails as $contact) {
1450 $contactIds[] = $contact['contact_id'];
1451 }
1452
ddeafe59
EM
1453 $activityParams['sms_text_message'] = 'text';
1454 $activityParams['activity_subject'] = 'subject';
1a7f0799 1455
1a7f0799
MW
1456 // Get a "logged in" user to set as source of Sms.
1457 $session = CRM_Core_Session::singleton();
1458 $sourceContactId = $session->get('userID');
1459
ddeafe59 1460 $this->createLoggedInUser();
a627697d
MW
1461
1462 // Give user permission to 'send SMS'
9099cab3 1463 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM', 'send SMS'];
a627697d
MW
1464
1465 // Create a phone number
1a7f0799
MW
1466 switch ($phoneType) {
1467 case 0:
1468 // No phone number
1469 break;
1470
1471 case 2:
1472 // Create a mobile phone number
ddeafe59 1473 $contactDetails = $this->createMobilePhone($contactId, $passPhoneTypeInContactDetails, $contactDetails);
1a7f0799
MW
1474 break;
1475
1476 case 1:
1477 // Create a fixed phone number
9099cab3 1478 $phone = civicrm_api3('Phone', 'create', [
1a7f0799
MW
1479 'contact_id' => $contactId,
1480 'phone' => 654321,
ddeafe59 1481 'phone_type_id' => 'Phone',
9099cab3 1482 ]);
a9b7ee41
SL
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 }
1a7f0799
MW
1487 break;
1488 }
1489
1490 // Now run the actual test
ddeafe59 1491 [$sent, $activityId, $success] = CRM_Activity_BAO_Activity::sendSms(
1a7f0799
MW
1492 $contactDetails,
1493 $activityParams,
1494 $smsProviderParams,
1495 $contactIds,
1496 $sourceContactId
1497 );
ddeafe59
EM
1498 $this->validateActivity($activityId);
1499 $this->assertEquals($expectSuccess, $success);
1500 return (array) $sent;
1a7f0799
MW
1501 }
1502
ddeafe59
EM
1503 /**
1504 * @throws \CRM_Core_Exception
1505 */
1506 protected function createTestActivities(): void {
1507 $this->loadXMLDataSet(__DIR__ . '/activities_for_dashboard_count.xml');
c2ce41b6 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']);
a6c2ebdc 1510 }
1511
5123cb11
SL
1512 /**
1513 * ACL HOOK implementation for various tests
1514 */
ddeafe59 1515 public function hook_civicrm_aclWhereClause($type, &$tables, &$whereTables, &$contactID, &$where): void {
5123cb11
SL
1516 if (!empty($this->allowedContactsACL)) {
1517 $contact_id_list = implode(',', $this->allowedContactsACL);
1518 $where = " contact_a.id IN ($contact_id_list)";
1519 }
1520 }
1521
cb5108b8 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
f7f1cc3b 1529 $result = $this->callAPISuccess('Case', 'create', [
cb5108b8 1530 'contact_id' => $contactId,
1531 'case_type_id' => '1',
1532 'subject' => "my case",
1533 'status_id' => "Open",
f7f1cc3b 1534 ]);
cb5108b8 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
f7f1cc3b 1540 $this->assertTrue(strpos($html_message, 'id: ' . $caseId) !== 0);
cb5108b8 1541 $this->assertTrue(strpos($html_message, 'subject: my case') !== 0);
f7f1cc3b 1542 $caseTest->tearDown();
cb5108b8 1543 }
1544
1545 public function testSendEmailWithCaseId() {
f7f1cc3b
SL
1546 $caseTest = new CiviCaseTestCase();
1547 $caseTest->setUp();
cb5108b8 1548 // Create a contact and contactDetails array.
1549 $contactId = $this->individualCreate();
f7f1cc3b 1550 $contact = $this->callAPISuccess('Contact', 'get', ['id' => $contactId]);
cb5108b8 1551
1552 // create a logged in USER since the code references it for sendEmail user.
1553 $this->createLoggedInUser();
f7f1cc3b 1554 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['view all contacts', 'access CiviCRM', 'access all cases and activities', 'administer CiviCase'];
cb5108b8 1555 $session = CRM_Core_Session::singleton();
1556 $loggedInUser = $session->get('userID');
1557
cb5108b8 1558 // create a case for this user
f7f1cc3b 1559 $result = $this->callAPISuccess('Case', 'create', [
cb5108b8 1560 'contact_id' => $contactId,
1561 'case_type_id' => 1,
1562 'subject' => "my case",
1563 'status_id' => "Open",
f7f1cc3b 1564 ]);
cb5108b8 1565
1566 $caseId = $result['id'];
1567
f7f1cc3b
SL
1568 $subject = __FUNCTION__ . ' subject {case.subject}';
1569 $html = __FUNCTION__ . ' html {case.subject}';
cb5108b8 1570 $text = __FUNCTION__ . ' text';
cb5108b8 1571
f7f1cc3b 1572 $mut = new CiviMailUtils($this, TRUE);
ddeafe59 1573 [$sent, $activity_ids] = $email_result = CRM_Activity_BAO_Activity::sendEmail(
f7f1cc3b 1574 $contact['values'],
cb5108b8 1575 $subject,
1576 $text,
1577 $html,
f7f1cc3b
SL
1578 $contact['values'][$contactId]['email'],
1579 $loggedInUser,
cb5108b8 1580 $from = __FUNCTION__ . '@example.com',
cb5108b8 1581 NULL,
f7f1cc3b
SL
1582 NULL,
1583 NULL,
1584 [$contactId],
1585 NULL,
1586 NULL,
1587 NULL,
cb5108b8 1588 $caseId
1589 );
13ecf776 1590 $activity = $this->callAPISuccess('Activity', 'getsingle', ['id' => $activity_ids[0], 'return' => ['case_id']]);
f7f1cc3b
SL
1591 $this->assertEquals($caseId, $activity['case_id'][0], 'Activity case_id does not match.');
1592 $mut->checkMailLog(['subject my case']);
1593 $mut->stop();
cb5108b8 1594 }
1595
726e904b
TO
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
0952020a
EM
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
88289f2e 1691 /**
1692 * Same as testSendEmailWillReplaceTokensUniquelyForEachContact but with
1693 * 3 recipients and an attachment.
0952020a
EM
1694 *
1695 * @throws \CRM_Core_Exception
1696 * @throws \CiviCRM_API3_Exception
88289f2e 1697 */
0952020a 1698 public function testSendEmailWillReplaceTokensUniquelyForEachContact3(): void {
88289f2e 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.
0952020a 1704 $loggedInUser = $this->createLoggedInUser();
88289f2e 1705 $contact = $this->callAPISuccess('Contact', 'get', ['sequential' => 1, 'id' => ['IN' => [$contactId1, $contactId2, $contactId3]]]);
1706
88289f2e 1707 // Add contact tokens in subject, html , text.
1708 $subject = __FUNCTION__ . ' subject' . '{contact.display_name}';
1709 $html = __FUNCTION__ . ' html' . '{contact.display_name}';
0952020a
EM
1710 // Check the smarty doesn't mess stuff up.
1711 $text = ' text' . '{contact.display_name} {$contact.first_name}';
88289f2e 1712
1713 $filepath = Civi::paths()->getPath('[civicrm.files]/custom');
0952020a 1714 $fileName = 'test_email_create.txt';
88289f2e 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'],
0952020a
EM
1733 $loggedInUser,
1734 __FUNCTION__ . '@example.com',
88289f2e 1735 $attachments,
88289f2e 1736 NULL,
0952020a
EM
1737 NULL,
1738 array_column($contact['values'], 'id'),
1739 NULL,
1740 NULL,
1741 $this->getCampaignID()
88289f2e 1742 );
0952020a 1743 $result = $this->callAPISuccess('Activity', 'get', ['campaign_id' => $this->getCampaignID()]);
88289f2e 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
726e904b
TO
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
3e120a63
RLAR
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 [
ddeafe59
EM
1872 "name" => 'Open Case',
1873 "status" => 'Completed',
1874 "label" => 'Open Case',
3e120a63
RLAR
1875 "default_assignee_type" => "1",
1876 ],
1877 ],
1878 "caseRoles" => [
1879 [
1880 "name" => "Case Coordinator",
ddeafe59
EM
1881 "creator" => 1,
1882 "manager" => 1,
3e120a63
RLAR
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();
f2aaaf30 1922 Civi::$statics['CRM_Core_Component']['info']['CiviCase'] = new CRM_Case_Info('CiviCase', 'CRM_Case', 7);
3e120a63
RLAR
1923 Civi::$statics['CRM_Core_Component']['info']['CiviCase']->info['showActivitiesInCore'] = $val;
1924 }
1925
28743d2f 1926 /**
1927 * Test multiple variations of target and assignee contacts in create
1928 * and edit mode.
1929 *
1930 * @dataProvider targetAndAssigneeProvider
ddeafe59 1931 *
28743d2f 1932 * @param array $do_first
1933 * @param array $do_second
ddeafe59
EM
1934 *
1935 * @throws \CiviCRM_API3_Exception
28743d2f 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
939f1575 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
ddeafe59 1999 *
939f1575 2000 * @param array $do_first
2001 * @param array $do_second
ddeafe59
EM
2002 *
2003 * @throws \CRM_Core_Exception
2004 * @throws \CiviCRM_API3_Exception
939f1575 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
28743d2f 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
13ecf776 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 */
0952020a 2600 public function testSendEmailWithMultipleToRecipients(): void {
13ecf776 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
ddeafe59 2611 [$sent, $activityIds] = CRM_Activity_BAO_Activity::sendEmail(
13ecf776 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,
0952020a 2622 array_column($contacts['values'], 'id')
13ecf776 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
ddeafe59
EM
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
0952020a
EM
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
6a488035 2704}